DAW JSON Link
Loading...
Searching...
No Matches
daw_json_writer.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
12
15
16#if defined( DAW_JSON_HAS_REFLECTION )
17#include <daw/daw_concepts.h>
18#endif
19
20#include <daw/daw_move.h>
21#include <daw/traits/daw_traits_remove_cvref.h>
22
23#include <type_traits>
24#include <vector>
25
26namespace daw::json {
27 inline namespace DAW_JSON_VER {
28 namespace json_writer_details {
29 template<typename JsonClass, typename Value>
30 using json_write_value_class_t = typename daw::conditional_t<
31 std::is_same_v<use_default, JsonClass>,
32 json_details::ident_trait<json_details::json_deduced_type, Value>,
33 json_details::ident_trait<json_details::json_deduced_type,
34 JsonClass>>::type;
35
36 enum class json_writer_states : std::uint8_t {
40 json_writer_nothing,
44 json_writer_object,
48 json_writer_array
49 };
50 } // namespace json_writer_details
51
52 template<typename WritableType, typename StackType, auto... PolicyFlags>
54 using iterator_t =
55 decltype( json_details::make_output_iterator<PolicyFlags...>(
56 std::declval<WritableType>( ) ) );
57
58 StackType m_stack{ };
59 json_writer_details::json_writer_states m_current_state =
60 json_writer_details::json_writer_states::json_writer_nothing;
61 bool m_is_first = true;
62 bool m_is_key_written = false;
64
65 constexpr void
66 push_state( json_writer_details::json_writer_states new_state ) {
67 m_stack.push_back( m_current_state );
68 m_current_state = new_state;
69 }
70
71 constexpr void pop_state( ) {
72 daw_json_ensure( not m_stack.empty( ), ErrorReason::OutputError );
73 m_current_state = m_stack.back( );
74 m_stack.pop_back( );
75 }
76
77 constexpr void write_item_prefix( ) {
78 daw_json_ensure( not m_is_key_written, ErrorReason::OutputError );
79 if( not m_is_first ) {
80 m_writer.put( ',' );
81 } else {
82 m_is_first = false;
83 }
84 m_writer.next_member( );
85 }
86
87 constexpr void prepare_value( ) {
88 if( m_current_state ==
89 json_writer_details::json_writer_states::json_writer_nothing ) {
90 daw_json_ensure( m_is_first, ErrorReason::OutputError );
91 return;
92 }
93 if( m_current_state ==
94 json_writer_details::json_writer_states::json_writer_object ) {
95 daw_json_ensure( m_is_key_written, ErrorReason::OutputError );
96 m_is_key_written = false;
97 return;
98 }
99 if( m_current_state !=
100 json_writer_details::json_writer_states::json_writer_nothing ) {
101 write_item_prefix( );
102 }
103 }
104
105 template<typename JsonClass = use_default, typename T>
106 constexpr void do_write_value( T const &value ) {
107 using json_class_t = typename daw::conditional_t<
108 std::is_same_v<use_default, JsonClass>,
109 json_details::ident_trait<json_details::json_deduced_type, T>,
110 json_details::ident_trait<json_details::json_deduced_type,
111 JsonClass>>::type;
112 prepare_value( );
113 if constexpr( std::is_pointer_v<std::remove_reference_t<T>> ) {
114 if( not value ) {
115 m_writer.write( "null" );
116 } else {
117 if constexpr( std::is_convertible_v<T, char const *> ) {
118 m_writer = json_details::member_to_string<
120 m_writer, daw::string_view( value ) );
121 } else {
122 m_writer =
123 json_details::member_to_string<json_class_t>( m_writer, value );
124 }
125 }
126 } else {
127 m_writer =
128 json_details::member_to_string<json_class_t>( m_writer, value );
129 }
130 m_is_first = false;
131 }
132
133 public:
134 template<typename W>
135 explicit constexpr json_writer_t( W &&writer DAW_LIFETIME_BOUND )
136 : m_writer( json_details::make_output_iterator<PolicyFlags...>(
137 DAW_FWD( writer ) ) ) {}
138 json_writer_t( json_writer_t const & ) = delete;
140 json_writer_t( json_writer_t &&other ) = delete;
142
143 constexpr void finalize( ) {
144 if( m_is_key_written ) {
145 write_value( nullptr );
146 }
147 while( m_current_state !=
148 json_writer_details::json_writer_states::json_writer_nothing ) {
149 switch( m_current_state ) {
150 case json_writer_details::json_writer_states::json_writer_object:
151 close_object( );
152 break;
153 case json_writer_details::json_writer_states::json_writer_array:
154 close_array( );
155 break;
156 case json_writer_details::json_writer_states::json_writer_nothing:
157 default:
158 break;
159 }
160 }
161 }
162
163 constexpr void reset( ) {
164 finalize( );
165 m_current_state =
166 json_writer_details::json_writer_states::json_writer_nothing;
167 m_is_first = true;
168 m_is_key_written = false;
169 }
170
171 DAW_CPP20_CX_DTOR ~json_writer_t( ) noexcept(
172 noexcept( m_writer.put( ' ' ) ) ) {
173 finalize( );
174 }
175
176 constexpr void open_object( ) {
177 prepare_value( );
178 push_state(
179 json_writer_details::json_writer_states::json_writer_object );
180 m_writer.put( '{' );
181 m_writer.add_indent( );
182 m_is_first = true;
183 }
184
185 constexpr void close_object( ) {
187 m_current_state ==
188 json_writer_details::json_writer_states::json_writer_object,
189 ErrorReason::OutputError );
190 if( m_is_key_written ) {
191 write_value( nullptr );
192 }
193 m_writer.del_indent( );
194 if( not m_is_first ) {
195 if constexpr( iterator_t::output_trailing_comma ==
196 options::OutputTrailingComma::Yes ) {
197 m_writer.put( ',' );
198 }
199 m_writer.next_member( );
200 }
201 m_writer.put( '}' );
202 pop_state( );
203 m_is_first = false;
204 }
205
206 constexpr void open_array( ) {
207 prepare_value( );
208 push_state(
209 json_writer_details::json_writer_states::json_writer_array );
210 m_is_first = true;
211 m_writer.add_indent( );
212 m_writer.put( '[' );
213 }
214
215 constexpr void close_array( ) {
217 m_current_state ==
218 json_writer_details::json_writer_states::json_writer_array,
219 ErrorReason::OutputError );
220 m_writer.del_indent( );
221 if( not m_is_first ) {
222 if constexpr( iterator_t::output_trailing_comma ==
223 options::OutputTrailingComma::Yes ) {
224 m_writer.put( ',' );
225 }
226 m_writer.next_member( );
227 }
228 m_writer.put( ']' );
229 pop_state( );
230 m_is_first = false;
231 }
232
233 constexpr void write_value( std::nullptr_t ) {
234 write_null( );
235 }
236
237 template<typename JsonClass = use_default, typename T>
238 constexpr void write_value( T const &value ) {
240 m_current_state !=
241 json_writer_details::json_writer_states::json_writer_object or
242 m_is_key_written,
243 ErrorReason::OutputError );
245 m_current_state !=
246 json_writer_details::json_writer_states::json_writer_nothing or
247 m_is_first,
248 ErrorReason::OutputError );
249
250 do_write_value<JsonClass>( value );
251 }
252
253 template<typename T, typename T2, typename... Ts>
254 constexpr void write_value( T const &value, T2 const &value2,
255 Ts const &...values ) {
257 m_current_state !=
258 json_writer_details::json_writer_states::json_writer_object or
259 m_is_key_written,
260 ErrorReason::OutputError );
262 m_current_state !=
263 json_writer_details::json_writer_states::json_writer_nothing or
264 m_is_first,
265 ErrorReason::OutputError );
266 open_array( );
267 write_array_values( value, value2, values... );
268 close_array( );
269 }
270
271 template<typename JsonClass = use_default, typename T>
272 constexpr void write_value( std::initializer_list<T> const &values ) {
274 m_current_state !=
275 json_writer_details::json_writer_states::json_writer_object or
276 m_is_key_written,
277 ErrorReason::OutputError );
279 m_current_state !=
280 json_writer_details::json_writer_states::json_writer_nothing or
281 m_is_first,
282 ErrorReason::OutputError );
283 open_array( );
284 write_array_values<JsonClass>( values );
285 close_array( );
286 }
287
288 template<typename JsonClass = use_default, std::size_t N>
289 constexpr void write_value( char const ( &str )[N] ) {
291 m_current_state !=
292 json_writer_details::json_writer_states::json_writer_object or
293 m_is_key_written,
294 ErrorReason::OutputError );
296 m_current_state !=
297 json_writer_details::json_writer_states::json_writer_nothing or
298 m_is_first,
299 ErrorReason::OutputError );
300 do_write_value<JsonClass>( daw::string_view( str, N - 1 ) );
301 }
302
303 constexpr void add_key( daw::string_view name ) {
304 if( m_is_key_written ) {
305 write_value( nullptr );
306 }
308 m_current_state ==
309 json_writer_details::json_writer_states::json_writer_object,
310 ErrorReason::OutputError );
311
312 write_item_prefix( );
313 m_writer.write( "\"", name, "\":", m_writer.space );
314 m_is_key_written = true;
315 }
316
317 constexpr void write_key_value( daw::string_view name, std::nullptr_t ) {
318 add_key( name );
319 write_null( );
320 }
321
322 template<typename JsonClass = use_default, typename T>
323 constexpr void write_key_value( daw::string_view name,
324 std::initializer_list<T> const &values ) {
325 add_key( name );
326 open_array( );
327 write_array_values<JsonClass>( values );
328 close_array( );
329 }
330
331 template<typename JsonClass = use_default, typename T>
332 constexpr void write_key_value( daw::string_view name, T const &value ) {
333 add_key( name );
334 write_value<JsonClass>( value );
335 }
336
337 template<typename T, typename T2, typename... Ts>
338 constexpr void write_key_value( daw::string_view name, T const &value,
339 T2 const &value2, Ts const &...values ) {
340 add_key( name );
341 open_array( );
342 write_array_values( value, value2, values... );
343 close_array( );
344 }
345
346 template<typename JsonClass = use_default, std::size_t N>
347 constexpr void write_key_value( daw::string_view name,
348 char const ( &str )[N] ) {
349 add_key( name );
350 write_value<JsonClass>( daw::string_view( str, N - 1 ) );
351 }
352
353 // Must be in array state
354 template<
355 typename JsonClass = use_default,
356 typename Range DAW_ENABLEIF( daw::traits::is_container_like_v<Range> )>
357 DAW_REQUIRES( daw::traits::is_container_like_v<Range> )
358 constexpr void write_array_values( Range const &values ) {
360 m_current_state ==
361 json_writer_details::json_writer_states::json_writer_array,
362 ErrorReason::OutputError );
363 for( auto const &value : values ) {
364 do_write_value<JsonClass>( value );
365 }
366 }
367
368 template<typename JsonClass = use_default, typename T>
369 constexpr void
370 write_array_values( std::initializer_list<T> const &values ) {
372 m_current_state ==
373 json_writer_details::json_writer_states::json_writer_array,
374 ErrorReason::OutputError );
375 for( auto const &value : values ) {
376 write_value<JsonClass>( value );
377 }
378 }
379
380 template<typename T, typename... Ts>
381 constexpr void write_array_values( T const &value, Ts const &...values ) {
383 m_current_state ==
384 json_writer_details::json_writer_states::json_writer_array,
385 ErrorReason::OutputError );
386 write_value( value );
387 (void)( ( write_value( values ), true ) and ... );
388 }
389
390 constexpr void write_boolean( bool b ) {
391 write_value( b );
392 }
393
394 constexpr void write_null( ) {
396 m_current_state !=
397 json_writer_details::json_writer_states::json_writer_nothing or
398 m_is_first,
399 ErrorReason::OutputError );
400 prepare_value( );
401 m_writer.write( "null" );
402 m_is_first = false;
403 }
404
405 template<typename JsonClass = use_default, typename T>
406 constexpr void write_number( T const &value ) {
407 using JsonMember =
408 json_writer_details::json_write_value_class_t<JsonClass, T>;
409 DAW_CPP23_STATIC_LOCAL constexpr JsonBaseParseTypes json_base_type =
410 JsonMember::underlying_json_type;
411 static_assert(
412 json_base_type == JsonBaseParseTypes::Number or
413 json_base_type == JsonBaseParseTypes::Bool,
414 "The underlying mapping must be a number or boolean type" );
415 if constexpr( json_base_type == JsonBaseParseTypes::Bool ) {
416 write_value<JsonClass>( static_cast<bool>( value ) ? 1 : 0 );
417 return;
418 } else {
419 write_value<JsonClass>( value );
420 }
421 }
422
423 private:
424 template<typename T>
425 DAW_CPP20_CX_ALLOC void write_string_class( T const &value ) {
426 using json_class_t = json_details::json_deduced_type<T>;
427 auto tmp = std::string{ };
428 auto tmp_iterator =
429 json_details::make_output_iterator<PolicyFlags...>( tmp );
430 tmp_iterator.indentation_level = m_writer.indentation_level;
431 (void)json_details::member_to_string<json_class_t>( tmp_iterator,
432 value );
433 do_write_value<json_string_no_name<>>( tmp );
434 }
435
436 public:
437 template<typename JsonClass = use_default, typename T>
438 constexpr void write_string( T const &value ) {
439 using JsonMember =
440 json_writer_details::json_write_value_class_t<JsonClass, T>;
441 DAW_CPP23_STATIC_LOCAL constexpr JsonBaseParseTypes json_base_type =
442 JsonMember::underlying_json_type;
443 if constexpr( json_base_type == JsonBaseParseTypes::String ) {
444 write_value<JsonClass>( value );
445 } else if constexpr( json_base_type == JsonBaseParseTypes::Bool or
446 json_base_type == JsonBaseParseTypes::Number ) {
447 prepare_value( );
448 m_writer.put( '"' );
449 m_writer =
450 json_details::member_to_string<JsonMember>( m_writer, value );
451 m_writer.put( '"' );
452 m_is_first = false;
453 } else {
454 write_string_class( value );
455 }
456 }
457
458 template<typename JsonClass = use_default, std::size_t N>
459 constexpr void write_string( char const ( &str )[N] ) {
460 write_value<JsonClass>( daw::string_view( str, N - 1 ) );
461 }
462
463#if defined( DAW_JSON_HAS_REFLECTION )
464 constexpr void write_enum_string( daw::EnumType auto e ) {
465 write_string( refl_details::enum_to_string( e ) );
466 }
467#endif
468 };
469
470 template<auto... PolicyFlags, typename WriterType>
471 constexpr auto json_writer( WriterType &&writer DAW_LIFETIME_BOUND ) {
473 std::vector<json_writer_details::json_writer_states>,
474 PolicyFlags...>( writer );
475 }
476 } // namespace DAW_JSON_VER
477} // namespace daw::json
DAW_CPP20_CX_DTOR ~json_writer_t() noexcept(noexcept(m_writer.put(' ')))
constexpr void write_array_values(std::initializer_list< T > const &values)
constexpr void write_key_value(daw::string_view name, std::nullptr_t)
json_writer_t & operator=(json_writer_t &&)=delete
json_writer_t & operator=(json_writer_t const &)=delete
constexpr void write_value(T const &value, T2 const &value2, Ts const &...values)
constexpr void push_state(json_writer_details::json_writer_states new_state)
constexpr void write_key_value(daw::string_view name, std::initializer_list< T > const &values)
constexpr void write_value(std::initializer_list< T > const &values)
constexpr void write_key_value(daw::string_view name, char const (&str)[N])
constexpr void write_array_values(T const &value, Ts const &...values)
decltype(json_details::make_output_iterator< PolicyFlags... >(std::declval< WritableType >())) iterator_t
constexpr void write_key_value(daw::string_view name, T const &value, T2 const &value2, Ts const &...values)
constexpr void write_key_value(daw::string_view name, T const &value)
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
DAW_REQUIRES(daw::json::json_details::is_container_opted_into_json_iostreams_v< Container >) std
An opt in ostream interface for containers of types that have JSON mappings.
constexpr auto json_writer(WriterType &&writer DAW_LIFETIME_BOUND)
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20