/src/jsoncons/include/jsoncons/json_encoder.hpp
Line | Count | Source |
1 | | // Copyright 2013-2025 Daniel Parker |
2 | | // Distributed under the Boost license, Version 1.0. |
3 | | // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
4 | | |
5 | | // See https://github.com/danielaparker/jsoncons for latest version |
6 | | |
7 | | #ifndef JSONCONS_JSON_ENCODER_HPP |
8 | | #define JSONCONS_JSON_ENCODER_HPP |
9 | | |
10 | | #include <array> // std::array |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <cmath> // std::isfinite, std::isnan |
14 | | #include <limits> // std::numeric_limits |
15 | | #include <memory> |
16 | | #include <string> |
17 | | #include <utility> // std::move |
18 | | #include <vector> |
19 | | |
20 | | #include <jsoncons/config/compiler_support.hpp> |
21 | | #include <jsoncons/config/jsoncons_config.hpp> |
22 | | #include <jsoncons/utility/write_number.hpp> |
23 | | #include <jsoncons/json_error.hpp> |
24 | | #include <jsoncons/json_exception.hpp> |
25 | | #include <jsoncons/json_options.hpp> |
26 | | #include <jsoncons/json_type.hpp> |
27 | | #include <jsoncons/json_visitor.hpp> |
28 | | #include <jsoncons/semantic_tag.hpp> |
29 | | #include <jsoncons/ser_util.hpp> |
30 | | #include <jsoncons/sink.hpp> |
31 | | #include <jsoncons/utility/bigint.hpp> |
32 | | #include <jsoncons/utility/byte_string.hpp> |
33 | | #include <jsoncons/utility/unicode_traits.hpp> |
34 | | |
35 | | namespace jsoncons { |
36 | | namespace detail { |
37 | | |
38 | | inline |
39 | | bool is_control_character(uint32_t c) |
40 | 0 | { |
41 | 0 | return c <= 0x1F || c == 0x7f; |
42 | 0 | } |
43 | | |
44 | | inline |
45 | | bool is_non_ascii_codepoint(uint32_t cp) |
46 | 0 | { |
47 | 0 | return cp >= 0x80; |
48 | 0 | } |
49 | | |
50 | | template <typename CharT,typename Sink> |
51 | | std::size_t escape_string(const CharT* s, std::size_t length, |
52 | | bool escape_all_non_ascii, bool escape_solidus, |
53 | | Sink& sink) |
54 | | { |
55 | | std::size_t count = 0; |
56 | | const CharT* begin = s; |
57 | | const CharT* end = s + length; |
58 | | for (const CharT* it = begin; it != end; ++it) |
59 | | { |
60 | | CharT c = *it; |
61 | | switch (c) |
62 | | { |
63 | | case '\\': |
64 | | sink.push_back('\\'); |
65 | | sink.push_back('\\'); |
66 | | count += 2; |
67 | | break; |
68 | | case '"': |
69 | | sink.push_back('\\'); |
70 | | sink.push_back('\"'); |
71 | | count += 2; |
72 | | break; |
73 | | case '\b': |
74 | | sink.push_back('\\'); |
75 | | sink.push_back('b'); |
76 | | count += 2; |
77 | | break; |
78 | | case '\f': |
79 | | sink.push_back('\\'); |
80 | | sink.push_back('f'); |
81 | | count += 2; |
82 | | break; |
83 | | case '\n': |
84 | | sink.push_back('\\'); |
85 | | sink.push_back('n'); |
86 | | count += 2; |
87 | | break; |
88 | | case '\r': |
89 | | sink.push_back('\\'); |
90 | | sink.push_back('r'); |
91 | | count += 2; |
92 | | break; |
93 | | case '\t': |
94 | | sink.push_back('\\'); |
95 | | sink.push_back('t'); |
96 | | count += 2; |
97 | | break; |
98 | | default: |
99 | | if (escape_solidus && c == '/') |
100 | | { |
101 | | sink.push_back('\\'); |
102 | | sink.push_back('/'); |
103 | | count += 2; |
104 | | } |
105 | | else if (is_control_character(c) || escape_all_non_ascii) |
106 | | { |
107 | | // convert to codepoint |
108 | | uint32_t cp; |
109 | | auto r = unicode_traits::to_codepoint(it, end, cp, unicode_traits::conv_flags::strict); |
110 | | if (r.ec != unicode_traits::conv_errc()) |
111 | | { |
112 | | JSONCONS_THROW(ser_error(json_errc::illegal_codepoint)); |
113 | | } |
114 | | it = r.ptr - 1; |
115 | | if (is_non_ascii_codepoint(cp) || is_control_character(c)) |
116 | | { |
117 | | if (cp > 0xFFFF) |
118 | | { |
119 | | cp -= 0x10000; |
120 | | uint32_t first = (cp >> 10) + 0xD800; |
121 | | uint32_t second = ((cp & 0x03FF) + 0xDC00); |
122 | | |
123 | | sink.push_back('\\'); |
124 | | sink.push_back('u'); |
125 | | sink.push_back(jsoncons::to_hex_character(first >> 12 & 0x000F)); |
126 | | sink.push_back(jsoncons::to_hex_character(first >> 8 & 0x000F)); |
127 | | sink.push_back(jsoncons::to_hex_character(first >> 4 & 0x000F)); |
128 | | sink.push_back(jsoncons::to_hex_character(first & 0x000F)); |
129 | | sink.push_back('\\'); |
130 | | sink.push_back('u'); |
131 | | sink.push_back(jsoncons::to_hex_character(second >> 12 & 0x000F)); |
132 | | sink.push_back(jsoncons::to_hex_character(second >> 8 & 0x000F)); |
133 | | sink.push_back(jsoncons::to_hex_character(second >> 4 & 0x000F)); |
134 | | sink.push_back(jsoncons::to_hex_character(second & 0x000F)); |
135 | | count += 12; |
136 | | } |
137 | | else |
138 | | { |
139 | | sink.push_back('\\'); |
140 | | sink.push_back('u'); |
141 | | sink.push_back(jsoncons::to_hex_character(cp >> 12 & 0x000F)); |
142 | | sink.push_back(jsoncons::to_hex_character(cp >> 8 & 0x000F)); |
143 | | sink.push_back(jsoncons::to_hex_character(cp >> 4 & 0x000F)); |
144 | | sink.push_back(jsoncons::to_hex_character(cp & 0x000F)); |
145 | | count += 6; |
146 | | } |
147 | | } |
148 | | else |
149 | | { |
150 | | sink.push_back(c); |
151 | | ++count; |
152 | | } |
153 | | } |
154 | | else |
155 | | { |
156 | | sink.push_back(c); |
157 | | ++count; |
158 | | } |
159 | | break; |
160 | | } |
161 | | } |
162 | | return count; |
163 | | } |
164 | | |
165 | | inline |
166 | | byte_string_chars_format resolve_byte_string_chars_format(byte_string_chars_format format1, |
167 | | byte_string_chars_format format2, |
168 | | byte_string_chars_format default_format = byte_string_chars_format::base64url) |
169 | 0 | { |
170 | 0 | byte_string_chars_format sink; |
171 | 0 | switch (format1) |
172 | 0 | { |
173 | 0 | case byte_string_chars_format::base16: |
174 | 0 | case byte_string_chars_format::base64: |
175 | 0 | case byte_string_chars_format::base64url: |
176 | 0 | sink = format1; |
177 | 0 | break; |
178 | 0 | default: |
179 | 0 | switch (format2) |
180 | 0 | { |
181 | 0 | case byte_string_chars_format::base64url: |
182 | 0 | case byte_string_chars_format::base64: |
183 | 0 | case byte_string_chars_format::base16: |
184 | 0 | sink = format2; |
185 | 0 | break; |
186 | 0 | default: // base64url |
187 | 0 | { |
188 | 0 | sink = default_format; |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | } |
192 | 0 | break; |
193 | 0 | } |
194 | 0 | return sink; |
195 | 0 | } |
196 | | |
197 | | } // namespace detail |
198 | | |
199 | | template <typename CharT,typename Sink=jsoncons::stream_sink<CharT>,typename Allocator=std::allocator<char>> |
200 | | class basic_json_encoder final : public basic_json_visitor<CharT> |
201 | | { |
202 | | static const jsoncons::basic_string_view<CharT> null_constant() |
203 | | { |
204 | | static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "null"); |
205 | | return k; |
206 | | } |
207 | | static const jsoncons::basic_string_view<CharT> true_constant() |
208 | | { |
209 | | static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "true"); |
210 | | return k; |
211 | | } |
212 | | static const jsoncons::basic_string_view<CharT> false_constant() |
213 | | { |
214 | | static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "false"); |
215 | | return k; |
216 | | } |
217 | | |
218 | | static const std::array<CharT,1> colon; |
219 | | static const std::array<CharT,2> colon_space; |
220 | | static const std::array<CharT,2> space_colon; |
221 | | static const std::array<CharT,3> space_colon_space; |
222 | | static const std::array<CharT,1> comma; |
223 | | static const std::array<CharT,2> comma_space; |
224 | | static const std::array<CharT,2> space_comma; |
225 | | static const std::array<CharT,3> space_comma_space; |
226 | | static const std::array<CharT,1> left_brace; |
227 | | static const std::array<CharT,1> right_brace; |
228 | | static const std::array<CharT,2> left_brace_space; |
229 | | static const std::array<CharT,2> space_right_brace; |
230 | | static const std::array<CharT,1> left_bracket; |
231 | | static const std::array<CharT,1> right_bracket; |
232 | | static const std::array<CharT,2> left_bracket_space; |
233 | | static const std::array<CharT,2> space_right_bracket; |
234 | | public: |
235 | | using allocator_type = Allocator; |
236 | | using char_type = CharT; |
237 | | using typename basic_json_visitor<CharT>::string_view_type; |
238 | | using sink_type = Sink; |
239 | | using string_type = typename basic_json_encode_options<CharT>::string_type; |
240 | | |
241 | | private: |
242 | | enum class container_type {object, array}; |
243 | | |
244 | | class encoding_context |
245 | | { |
246 | | container_type type_; |
247 | | line_split_kind split_kind_; |
248 | | bool indent_before_; |
249 | | bool new_line_after_; |
250 | | std::size_t begin_pos_{0}; |
251 | | std::size_t data_pos_{0}; |
252 | | std::size_t count_{0}; |
253 | | public: |
254 | | encoding_context(container_type type, line_split_kind split_lines, bool indent_once, |
255 | | std::size_t begin_pos, std::size_t data_pos) noexcept |
256 | | : type_(type), split_kind_(split_lines), indent_before_(indent_once), new_line_after_(false), |
257 | | begin_pos_(begin_pos), data_pos_(data_pos) |
258 | | { |
259 | | } |
260 | | |
261 | | encoding_context(const encoding_context&) = default; |
262 | | |
263 | | ~encoding_context() = default; |
264 | | |
265 | | encoding_context& operator=(const encoding_context&) = default; |
266 | | |
267 | | void set_position(std::size_t pos) |
268 | | { |
269 | | data_pos_ = pos; |
270 | | } |
271 | | |
272 | | std::size_t begin_pos() const |
273 | | { |
274 | | return begin_pos_; |
275 | | } |
276 | | |
277 | | std::size_t data_pos() const |
278 | | { |
279 | | return data_pos_; |
280 | | } |
281 | | |
282 | | std::size_t count() const |
283 | | { |
284 | | return count_; |
285 | | } |
286 | | |
287 | | void increment_count() |
288 | | { |
289 | | ++count_; |
290 | | } |
291 | | |
292 | | bool new_line_after() const |
293 | | { |
294 | | return new_line_after_; |
295 | | } |
296 | | |
297 | | void new_line_after(bool value) |
298 | | { |
299 | | new_line_after_ = value; |
300 | | } |
301 | | |
302 | | bool is_object() const |
303 | | { |
304 | | return type_ == container_type::object; |
305 | | } |
306 | | |
307 | | bool is_array() const |
308 | | { |
309 | | return type_ == container_type::array; |
310 | | } |
311 | | |
312 | | line_split_kind split_kind() const |
313 | | { |
314 | | return split_kind_; |
315 | | } |
316 | | |
317 | | bool is_multi_line() const |
318 | | { |
319 | | return split_kind_ == line_split_kind::multi_line; |
320 | | } |
321 | | |
322 | | bool is_indent_once() const |
323 | | { |
324 | | return count_ == 0 ? indent_before_ : false; |
325 | | } |
326 | | |
327 | | }; |
328 | | using encoding_context_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context>; |
329 | | |
330 | | Sink sink_; |
331 | | basic_json_encode_options<CharT> options_; |
332 | | char_type indent_char_{' '}; |
333 | | jsoncons::write_double fp_; |
334 | | |
335 | | std::vector<encoding_context,encoding_context_allocator_type> stack_; |
336 | | int indent_amount_{0}; |
337 | | std::size_t column_{0}; |
338 | | jsoncons::basic_string_view<CharT> colon_str_; |
339 | | jsoncons::basic_string_view<CharT> comma_str_; |
340 | | jsoncons::basic_string_view<CharT> open_brace_str_; |
341 | | jsoncons::basic_string_view<CharT> close_brace_str_; |
342 | | jsoncons::basic_string_view<CharT> open_bracket_str_; |
343 | | jsoncons::basic_string_view<CharT> close_bracket_str_; |
344 | | int nesting_depth_{0}; |
345 | | public: |
346 | | |
347 | | // Noncopyable and nonmoveable |
348 | | basic_json_encoder(const basic_json_encoder&) = delete; |
349 | | basic_json_encoder(basic_json_encoder&&) = delete; |
350 | | |
351 | | basic_json_encoder(Sink&& sink, |
352 | | const Allocator& alloc = Allocator()) |
353 | | : basic_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc) |
354 | | { |
355 | | } |
356 | | |
357 | | basic_json_encoder(Sink&& sink, |
358 | | const basic_json_encode_options<CharT>& options, |
359 | | const Allocator& alloc = Allocator()) |
360 | | : sink_(std::forward<Sink>(sink)), |
361 | | options_(options), |
362 | | indent_char_(options.indent_char()), |
363 | | fp_(options.float_format(), options.precision()), |
364 | | stack_(alloc) |
365 | | { |
366 | | switch (options.spaces_around_colon()) |
367 | | { |
368 | | case spaces_option::space_after: |
369 | | colon_str_ = jsoncons::basic_string_view<CharT>(colon_space.data(), colon_space.size()); |
370 | | break; |
371 | | case spaces_option::space_before: |
372 | | colon_str_ = jsoncons::basic_string_view<CharT>(space_colon.data(), space_colon.size()); |
373 | | break; |
374 | | case spaces_option::space_before_and_after: |
375 | | colon_str_ = jsoncons::basic_string_view<CharT>(space_colon_space.data(), space_colon_space.size()); |
376 | | break; |
377 | | default: |
378 | | colon_str_ = jsoncons::basic_string_view<CharT>(colon.data(), colon.size()); |
379 | | break; |
380 | | } |
381 | | switch (options.spaces_around_comma()) |
382 | | { |
383 | | case spaces_option::space_after: |
384 | | comma_str_ = jsoncons::basic_string_view<CharT>(comma_space.data(), colon_space.size()); |
385 | | break; |
386 | | case spaces_option::space_before: |
387 | | comma_str_ = jsoncons::basic_string_view<CharT>(space_comma.data(), space_comma.size()); |
388 | | break; |
389 | | case spaces_option::space_before_and_after: |
390 | | comma_str_ = jsoncons::basic_string_view<CharT>(space_comma_space.data(), space_comma_space.size()); |
391 | | break; |
392 | | default: |
393 | | comma_str_ = jsoncons::basic_string_view<CharT>(comma.data(), comma.size()); |
394 | | break; |
395 | | } |
396 | | if (options.pad_inside_object_braces()) |
397 | | { |
398 | | open_brace_str_ = jsoncons::basic_string_view<CharT>(left_brace_space.data(), left_brace_space.size()); |
399 | | close_brace_str_ = jsoncons::basic_string_view<CharT>(space_right_brace.data(), space_right_brace.size()); |
400 | | } |
401 | | else |
402 | | { |
403 | | open_brace_str_ = jsoncons::basic_string_view<CharT>(left_brace.data(), left_brace.size()); |
404 | | close_brace_str_ = jsoncons::basic_string_view<CharT>(right_brace.data(), right_brace.size()); |
405 | | } |
406 | | if (options.pad_inside_array_brackets()) |
407 | | { |
408 | | open_bracket_str_ = jsoncons::basic_string_view<CharT>(left_bracket_space.data(), left_bracket_space.size()); |
409 | | close_bracket_str_ = jsoncons::basic_string_view<CharT>(space_right_bracket.data(), space_right_bracket.size()); |
410 | | } |
411 | | else |
412 | | { |
413 | | open_bracket_str_ = jsoncons::basic_string_view<CharT>(left_bracket.data(), left_bracket.size()); |
414 | | close_bracket_str_ = jsoncons::basic_string_view<CharT>(right_bracket.data(), right_bracket.size()); |
415 | | } |
416 | | } |
417 | | |
418 | | ~basic_json_encoder() noexcept |
419 | | { |
420 | | JSONCONS_TRY |
421 | | { |
422 | | sink_.flush(); |
423 | | } |
424 | | JSONCONS_CATCH(...) |
425 | | { |
426 | | } |
427 | | } |
428 | | |
429 | | basic_json_encoder& operator=(const basic_json_encoder&) = delete; |
430 | | basic_json_encoder& operator=(basic_json_encoder&&) = delete; |
431 | | |
432 | | void reset() |
433 | | { |
434 | | stack_.clear(); |
435 | | indent_amount_ = 0; |
436 | | column_ = 0; |
437 | | nesting_depth_ = 0; |
438 | | } |
439 | | |
440 | | void reset(Sink&& sink) |
441 | | { |
442 | | sink_ = std::move(sink); |
443 | | reset(); |
444 | | } |
445 | | |
446 | | private: |
447 | | // Implementing methods |
448 | | void visit_flush() final |
449 | | { |
450 | | sink_.flush(); |
451 | | } |
452 | | |
453 | | JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) final |
454 | | { |
455 | | if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) |
456 | | { |
457 | | ec = json_errc::max_nesting_depth_exceeded; |
458 | | JSONCONS_VISITOR_RETURN; |
459 | | } |
460 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
461 | | { |
462 | | sink_.append(comma_str_.data(),comma_str_.length()); |
463 | | column_ += comma_str_.length(); |
464 | | } |
465 | | |
466 | | if (!stack_.empty()) // object or array |
467 | | { |
468 | | if (stack_.back().is_object()) |
469 | | { |
470 | | line_split_kind split_kind = static_cast<uint8_t>(options_.object_object_line_splits()) >= static_cast<uint8_t>(stack_.back().split_kind()) ? |
471 | | options_.object_object_line_splits() : stack_.back().split_kind(); |
472 | | switch (split_kind) |
473 | | { |
474 | | case line_split_kind::same_line: |
475 | | case line_split_kind::new_line: |
476 | | if (column_ >= options_.line_length_limit()) |
477 | | { |
478 | | break_line(); |
479 | | } |
480 | | break; |
481 | | default: // multi_line |
482 | | break; |
483 | | } |
484 | | stack_.emplace_back(container_type::object,split_kind, false, |
485 | | column_, column_+open_brace_str_.length()); |
486 | | } |
487 | | else // array |
488 | | { |
489 | | line_split_kind split_kind = static_cast<uint8_t>(options_.array_object_line_splits()) >= static_cast<uint8_t>(stack_.back().split_kind()) ? |
490 | | options_.array_object_line_splits() : stack_.back().split_kind(); |
491 | | switch (split_kind) |
492 | | { |
493 | | case line_split_kind::same_line: |
494 | | if (column_ >= options_.line_length_limit()) |
495 | | { |
496 | | //stack_.back().new_line_after(true); |
497 | | new_line(); |
498 | | } |
499 | | else |
500 | | { |
501 | | stack_.back().new_line_after(true); |
502 | | new_line(); |
503 | | } |
504 | | break; |
505 | | case line_split_kind::new_line: |
506 | | stack_.back().new_line_after(true); |
507 | | new_line(); |
508 | | break; |
509 | | default: // multi_line |
510 | | stack_.back().new_line_after(true); |
511 | | new_line(); |
512 | | break; |
513 | | } |
514 | | stack_.emplace_back(container_type::object,split_kind, false, |
515 | | column_, column_+open_brace_str_.length()); |
516 | | } |
517 | | } |
518 | | else |
519 | | { |
520 | | stack_.emplace_back(container_type::object, options_.root_line_splits(), false, |
521 | | column_, column_+open_brace_str_.length()); |
522 | | } |
523 | | indent(); |
524 | | |
525 | | sink_.append(open_brace_str_.data(), open_brace_str_.length()); |
526 | | column_ += open_brace_str_.length(); |
527 | | JSONCONS_VISITOR_RETURN; |
528 | | } |
529 | | |
530 | | JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) final |
531 | | { |
532 | | JSONCONS_ASSERT(!stack_.empty()); |
533 | | --nesting_depth_; |
534 | | |
535 | | unindent(); |
536 | | if (stack_.back().new_line_after()) |
537 | | { |
538 | | new_line(); |
539 | | } |
540 | | stack_.pop_back(); |
541 | | sink_.append(close_brace_str_.data(), close_brace_str_.length()); |
542 | | column_ += close_brace_str_.length(); |
543 | | |
544 | | end_value(); |
545 | | JSONCONS_VISITOR_RETURN; |
546 | | } |
547 | | |
548 | | JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) final |
549 | | { |
550 | | if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) |
551 | | { |
552 | | ec = json_errc::max_nesting_depth_exceeded; |
553 | | JSONCONS_VISITOR_RETURN; |
554 | | } |
555 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
556 | | { |
557 | | sink_.append(comma_str_.data(),comma_str_.length()); |
558 | | column_ += comma_str_.length(); |
559 | | } |
560 | | if (!stack_.empty()) |
561 | | { |
562 | | if (stack_.back().is_object()) |
563 | | { |
564 | | line_split_kind split_kind = static_cast<uint8_t>(options_.object_array_line_splits()) >= static_cast<uint8_t>(stack_.back().split_kind()) ? |
565 | | options_.object_array_line_splits() : |
566 | | stack_.back().split_kind(); |
567 | | switch (split_kind) |
568 | | { |
569 | | case line_split_kind::same_line: |
570 | | stack_.emplace_back(container_type::array,split_kind,false, |
571 | | column_, column_ + open_bracket_str_.length()); |
572 | | break; |
573 | | case line_split_kind::new_line: |
574 | | { |
575 | | stack_.emplace_back(container_type::array,split_kind,true, |
576 | | column_, column_+open_bracket_str_.length()); |
577 | | break; |
578 | | } |
579 | | default: // multi_line |
580 | | stack_.emplace_back(container_type::array,split_kind,true, |
581 | | column_, column_+open_bracket_str_.length()); |
582 | | break; |
583 | | } |
584 | | } |
585 | | else // array |
586 | | { |
587 | | line_split_kind split_kind = static_cast<uint8_t>(options_.array_array_line_splits()) >= static_cast<uint8_t>(stack_.back().split_kind()) ? |
588 | | options_.array_array_line_splits() : stack_.back().split_kind(); |
589 | | switch (split_kind) |
590 | | { |
591 | | case line_split_kind::same_line: |
592 | | if (stack_.back().is_multi_line()) |
593 | | { |
594 | | stack_.back().new_line_after(true); |
595 | | new_line(); |
596 | | } |
597 | | stack_.emplace_back(container_type::array,split_kind, false, |
598 | | column_, column_+open_bracket_str_.length()); |
599 | | break; |
600 | | case line_split_kind::new_line: |
601 | | stack_.back().new_line_after(true); |
602 | | new_line(); |
603 | | stack_.emplace_back(container_type::array,split_kind, true, |
604 | | column_, column_+open_bracket_str_.length()); |
605 | | break; |
606 | | default: // multi_line |
607 | | stack_.back().new_line_after(true); |
608 | | new_line(); |
609 | | stack_.emplace_back(container_type::array,split_kind, false, |
610 | | column_, column_+open_bracket_str_.length()); |
611 | | break; |
612 | | } |
613 | | } |
614 | | } |
615 | | else |
616 | | { |
617 | | stack_.emplace_back(container_type::array, options_.root_line_splits(), false, |
618 | | column_, column_+open_bracket_str_.length()); |
619 | | } |
620 | | indent(); |
621 | | sink_.append(open_bracket_str_.data(), open_bracket_str_.length()); |
622 | | column_ += open_bracket_str_.length(); |
623 | | JSONCONS_VISITOR_RETURN; |
624 | | } |
625 | | |
626 | | JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) final |
627 | | { |
628 | | JSONCONS_ASSERT(!stack_.empty()); |
629 | | --nesting_depth_; |
630 | | |
631 | | unindent(); |
632 | | if (stack_.back().new_line_after()) |
633 | | { |
634 | | new_line(); |
635 | | } |
636 | | stack_.pop_back(); |
637 | | sink_.append(close_bracket_str_.data(), close_bracket_str_.length()); |
638 | | column_ += close_bracket_str_.length(); |
639 | | end_value(); |
640 | | JSONCONS_VISITOR_RETURN; |
641 | | } |
642 | | |
643 | | JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code&) final |
644 | | { |
645 | | JSONCONS_ASSERT(!stack_.empty()); |
646 | | if (stack_.back().count() > 0) |
647 | | { |
648 | | sink_.append(comma_str_.data(),comma_str_.length()); |
649 | | column_ += comma_str_.length(); |
650 | | } |
651 | | |
652 | | if (stack_.back().is_multi_line()) |
653 | | { |
654 | | stack_.back().new_line_after(true); |
655 | | new_line(); |
656 | | } |
657 | | else if (stack_.back().count() > 0 && column_ >= options_.line_length_limit()) |
658 | | { |
659 | | //stack_.back().new_line_after(true); |
660 | | new_line(stack_.back().data_pos()); |
661 | | } |
662 | | |
663 | | if (stack_.back().count() == 0) |
664 | | { |
665 | | stack_.back().set_position(column_); |
666 | | } |
667 | | sink_.push_back('\"'); |
668 | | std::size_t length = jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); |
669 | | sink_.push_back('\"'); |
670 | | sink_.append(colon_str_.data(),colon_str_.length()); |
671 | | column_ += (length+2+colon_str_.length()); |
672 | | JSONCONS_VISITOR_RETURN; |
673 | | } |
674 | | |
675 | | JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) final |
676 | | { |
677 | | if (!stack_.empty()) |
678 | | { |
679 | | if (stack_.back().is_array()) |
680 | | { |
681 | | begin_scalar_value(); |
682 | | } |
683 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
684 | | { |
685 | | break_line(); |
686 | | } |
687 | | } |
688 | | |
689 | | sink_.append(null_constant().data(), null_constant().size()); |
690 | | column_ += null_constant().size(); |
691 | | |
692 | | end_value(); |
693 | | JSONCONS_VISITOR_RETURN; |
694 | | } |
695 | | |
696 | | JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) final |
697 | | { |
698 | | if (!stack_.empty()) |
699 | | { |
700 | | if (stack_.back().is_array()) |
701 | | { |
702 | | begin_scalar_value(); |
703 | | } |
704 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
705 | | { |
706 | | break_line(); |
707 | | } |
708 | | } |
709 | | |
710 | | write_string(sv, tag, context, ec); |
711 | | |
712 | | end_value(); |
713 | | JSONCONS_VISITOR_RETURN; |
714 | | } |
715 | | |
716 | | void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) |
717 | | { |
718 | | if (JSONCONS_LIKELY(tag == semantic_tag::noesc && !options_.escape_all_non_ascii() && !options_.escape_solidus())) |
719 | | { |
720 | | //std::cout << "noesc\n"; |
721 | | sink_.push_back('\"'); |
722 | | const CharT* begin = sv.data(); |
723 | | const CharT* end = begin + sv.length(); |
724 | | for (const CharT* it = begin; it != end; ++it) |
725 | | { |
726 | | sink_.push_back(*it); |
727 | | } |
728 | | sink_.push_back('\"'); |
729 | | column_ += (sv.length()+2); |
730 | | } |
731 | | else if (tag == semantic_tag::bigint) |
732 | | { |
733 | | write_bignum_value(sv); |
734 | | } |
735 | | else if (tag == semantic_tag::bigdec && options_.bignum_format() == bignum_format_kind::raw) |
736 | | { |
737 | | write_bignum_value(sv); |
738 | | } |
739 | | else |
740 | | { |
741 | | //if (tag != semantic_tag::bigdec) |
742 | | // std::cout << "esc\n"; |
743 | | sink_.push_back('\"'); |
744 | | std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); |
745 | | sink_.push_back('\"'); |
746 | | column_ += (length+2); |
747 | | } |
748 | | } |
749 | | |
750 | | JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, |
751 | | semantic_tag tag, |
752 | | const ser_context&, |
753 | | std::error_code&) final |
754 | | { |
755 | | if (!stack_.empty()) |
756 | | { |
757 | | if (stack_.back().is_array()) |
758 | | { |
759 | | begin_scalar_value(); |
760 | | } |
761 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
762 | | { |
763 | | break_line(); |
764 | | } |
765 | | } |
766 | | |
767 | | byte_string_chars_format encoding_hint; |
768 | | switch (tag) |
769 | | { |
770 | | case semantic_tag::base16: |
771 | | encoding_hint = byte_string_chars_format::base16; |
772 | | break; |
773 | | case semantic_tag::base64: |
774 | | encoding_hint = byte_string_chars_format::base64; |
775 | | break; |
776 | | case semantic_tag::base64url: |
777 | | encoding_hint = byte_string_chars_format::base64url; |
778 | | break; |
779 | | default: |
780 | | encoding_hint = byte_string_chars_format::none; |
781 | | break; |
782 | | } |
783 | | |
784 | | byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), |
785 | | encoding_hint, |
786 | | byte_string_chars_format::base64url); |
787 | | switch (format) |
788 | | { |
789 | | case byte_string_chars_format::base16: |
790 | | { |
791 | | sink_.push_back('\"'); |
792 | | std::size_t length = bytes_to_base16(b.begin(),b.end(),sink_); |
793 | | sink_.push_back('\"'); |
794 | | column_ += (length + 2); |
795 | | break; |
796 | | } |
797 | | case byte_string_chars_format::base64: |
798 | | { |
799 | | sink_.push_back('\"'); |
800 | | std::size_t length = bytes_to_base64(b.begin(), b.end(), sink_); |
801 | | sink_.push_back('\"'); |
802 | | column_ += (length + 2); |
803 | | break; |
804 | | } |
805 | | case byte_string_chars_format::base64url: |
806 | | { |
807 | | sink_.push_back('\"'); |
808 | | std::size_t length = bytes_to_base64url(b.begin(),b.end(),sink_); |
809 | | sink_.push_back('\"'); |
810 | | column_ += (length + 2); |
811 | | break; |
812 | | } |
813 | | default: |
814 | | { |
815 | | JSONCONS_UNREACHABLE(); |
816 | | } |
817 | | } |
818 | | |
819 | | end_value(); |
820 | | JSONCONS_VISITOR_RETURN; |
821 | | } |
822 | | |
823 | | JSONCONS_VISITOR_RETURN_TYPE visit_double(double value, |
824 | | semantic_tag, |
825 | | const ser_context& context, |
826 | | std::error_code& ec) final |
827 | | { |
828 | | if (!stack_.empty()) |
829 | | { |
830 | | if (stack_.back().is_array()) |
831 | | { |
832 | | begin_scalar_value(); |
833 | | } |
834 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
835 | | { |
836 | | break_line(); |
837 | | } |
838 | | } |
839 | | |
840 | | if (!std::isfinite(value)) |
841 | | { |
842 | | if ((std::isnan)(value)) |
843 | | { |
844 | | if (options_.enable_nan_to_num()) |
845 | | { |
846 | | sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length()); |
847 | | column_ += options_.nan_to_num().length(); |
848 | | } |
849 | | else if (options_.enable_nan_to_str()) |
850 | | { |
851 | | write_string(options_.nan_to_str(), semantic_tag::none, context, ec); |
852 | | } |
853 | | else |
854 | | { |
855 | | sink_.append(null_constant().data(), null_constant().size()); |
856 | | column_ += null_constant().size(); |
857 | | } |
858 | | } |
859 | | else if (value == std::numeric_limits<double>::infinity()) |
860 | | { |
861 | | if (options_.enable_inf_to_num()) |
862 | | { |
863 | | sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length()); |
864 | | column_ += options_.inf_to_num().length(); |
865 | | } |
866 | | else if (options_.enable_inf_to_str()) |
867 | | { |
868 | | write_string(options_.inf_to_str(), semantic_tag::none, context, ec); |
869 | | } |
870 | | else |
871 | | { |
872 | | sink_.append(null_constant().data(), null_constant().size()); |
873 | | column_ += null_constant().size(); |
874 | | } |
875 | | } |
876 | | else |
877 | | { |
878 | | if (options_.enable_neginf_to_num()) |
879 | | { |
880 | | sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length()); |
881 | | column_ += options_.neginf_to_num().length(); |
882 | | } |
883 | | else if (options_.enable_neginf_to_str()) |
884 | | { |
885 | | write_string(options_.neginf_to_str(), semantic_tag::none, context, ec); |
886 | | } |
887 | | else |
888 | | { |
889 | | sink_.append(null_constant().data(), null_constant().size()); |
890 | | column_ += null_constant().size(); |
891 | | } |
892 | | } |
893 | | } |
894 | | else |
895 | | { |
896 | | std::size_t length = fp_(value, sink_); |
897 | | column_ += length; |
898 | | } |
899 | | |
900 | | end_value(); |
901 | | JSONCONS_VISITOR_RETURN; |
902 | | } |
903 | | |
904 | | JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t value, |
905 | | semantic_tag, |
906 | | const ser_context&, |
907 | | std::error_code&) final |
908 | | { |
909 | | if (!stack_.empty()) |
910 | | { |
911 | | if (stack_.back().is_array()) |
912 | | { |
913 | | begin_scalar_value(); |
914 | | } |
915 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
916 | | { |
917 | | break_line(); |
918 | | } |
919 | | } |
920 | | std::size_t length = jsoncons::from_integer(value, sink_); |
921 | | column_ += length; |
922 | | end_value(); |
923 | | JSONCONS_VISITOR_RETURN; |
924 | | } |
925 | | |
926 | | JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t value, |
927 | | semantic_tag, |
928 | | const ser_context&, |
929 | | std::error_code&) final |
930 | | { |
931 | | if (!stack_.empty()) |
932 | | { |
933 | | if (stack_.back().is_array()) |
934 | | { |
935 | | begin_scalar_value(); |
936 | | } |
937 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
938 | | { |
939 | | break_line(); |
940 | | } |
941 | | } |
942 | | std::size_t length = jsoncons::from_integer(value, sink_); |
943 | | column_ += length; |
944 | | end_value(); |
945 | | JSONCONS_VISITOR_RETURN; |
946 | | } |
947 | | |
948 | | JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) final |
949 | | { |
950 | | if (!stack_.empty()) |
951 | | { |
952 | | if (stack_.back().is_array()) |
953 | | { |
954 | | begin_scalar_value(); |
955 | | } |
956 | | if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) |
957 | | { |
958 | | break_line(); |
959 | | } |
960 | | } |
961 | | |
962 | | if (value) |
963 | | { |
964 | | sink_.append(true_constant().data(), true_constant().size()); |
965 | | column_ += true_constant().size(); |
966 | | } |
967 | | else |
968 | | { |
969 | | sink_.append(false_constant().data(), false_constant().size()); |
970 | | column_ += false_constant().size(); |
971 | | } |
972 | | |
973 | | end_value(); |
974 | | JSONCONS_VISITOR_RETURN; |
975 | | } |
976 | | |
977 | | void begin_scalar_value() |
978 | | { |
979 | | if (!stack_.empty()) |
980 | | { |
981 | | if (stack_.back().count() > 0) |
982 | | { |
983 | | sink_.append(comma_str_.data(),comma_str_.length()); |
984 | | column_ += comma_str_.length(); |
985 | | } |
986 | | if (stack_.back().is_multi_line() || stack_.back().is_indent_once()) |
987 | | { |
988 | | stack_.back().new_line_after(true); |
989 | | new_line(); |
990 | | } |
991 | | } |
992 | | } |
993 | | |
994 | | void write_bignum_value(const string_view_type& sv) |
995 | | { |
996 | | switch (options_.bignum_format()) |
997 | | { |
998 | | case bignum_format_kind::raw: |
999 | | { |
1000 | | sink_.append(sv.data(),sv.size()); |
1001 | | column_ += sv.size(); |
1002 | | break; |
1003 | | } |
1004 | | case bignum_format_kind::base64: |
1005 | | { |
1006 | | bigint n(sv.data(), sv.length()); |
1007 | | bool is_neg = n < 0; |
1008 | | if (is_neg) |
1009 | | { |
1010 | | n = - n -1; |
1011 | | } |
1012 | | int signum; |
1013 | | std::vector<uint8_t> v; |
1014 | | n.write_bytes_be(signum, v); |
1015 | | |
1016 | | sink_.push_back('\"'); |
1017 | | if (is_neg) |
1018 | | { |
1019 | | sink_.push_back('~'); |
1020 | | ++column_; |
1021 | | } |
1022 | | std::size_t length = bytes_to_base64(v.begin(), v.end(), sink_); |
1023 | | sink_.push_back('\"'); |
1024 | | column_ += (length+2); |
1025 | | break; |
1026 | | } |
1027 | | case bignum_format_kind::base64url: |
1028 | | { |
1029 | | bigint n(sv.data(), sv.length()); |
1030 | | bool is_neg = n < 0; |
1031 | | if (is_neg) |
1032 | | { |
1033 | | n = - n -1; |
1034 | | } |
1035 | | int signum; |
1036 | | std::vector<uint8_t> v; |
1037 | | n.write_bytes_be(signum, v); |
1038 | | |
1039 | | sink_.push_back('\"'); |
1040 | | if (is_neg) |
1041 | | { |
1042 | | sink_.push_back('~'); |
1043 | | ++column_; |
1044 | | } |
1045 | | std::size_t length = bytes_to_base64url(v.begin(), v.end(), sink_); |
1046 | | sink_.push_back('\"'); |
1047 | | column_ += (length+2); |
1048 | | break; |
1049 | | } |
1050 | | default: |
1051 | | { |
1052 | | sink_.push_back('\"'); |
1053 | | sink_.append(sv.data(),sv.size()); |
1054 | | sink_.push_back('\"'); |
1055 | | column_ += (sv.size() + 2); |
1056 | | break; |
1057 | | } |
1058 | | } |
1059 | | } |
1060 | | |
1061 | | void end_value() |
1062 | | { |
1063 | | if (!stack_.empty()) |
1064 | | { |
1065 | | stack_.back().increment_count(); |
1066 | | } |
1067 | | } |
1068 | | |
1069 | | void indent() |
1070 | | { |
1071 | | indent_amount_ += static_cast<uint8_t>(options_.indent_size()); |
1072 | | } |
1073 | | |
1074 | | void unindent() |
1075 | | { |
1076 | | indent_amount_ -= static_cast<uint8_t>(options_.indent_size()); |
1077 | | } |
1078 | | |
1079 | | void new_line() |
1080 | | { |
1081 | | sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length()); |
1082 | | for (int i = 0; i < indent_amount_; ++i) |
1083 | | { |
1084 | | sink_.push_back(indent_char_); |
1085 | | } |
1086 | | column_ = indent_amount_; |
1087 | | } |
1088 | | |
1089 | | void new_line(std::size_t len) |
1090 | | { |
1091 | | sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length()); |
1092 | | for (std::size_t i = 0; i < len; ++i) |
1093 | | { |
1094 | | sink_.push_back(' '); |
1095 | | } |
1096 | | column_ = len; |
1097 | | } |
1098 | | |
1099 | | void break_line() |
1100 | | { |
1101 | | stack_.back().new_line_after(true); |
1102 | | new_line(); |
1103 | | } |
1104 | | }; |
1105 | | |
1106 | | template <typename CharT, typename Sink, typename Allocator> |
1107 | | const std::array<CharT,1> basic_json_encoder<CharT, Sink, Allocator>::colon = {':'}; |
1108 | | template <typename CharT,typename Sink,typename Allocator> |
1109 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::colon_space = {':', ' '}; |
1110 | | template <typename CharT,typename Sink,typename Allocator> |
1111 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::space_colon = {' ', ':'}; |
1112 | | template <typename CharT,typename Sink,typename Allocator> |
1113 | | const std::array<CharT,3> basic_json_encoder<CharT,Sink,Allocator>::space_colon_space = {' ', ':', ' '}; |
1114 | | |
1115 | | template <typename CharT, typename Sink, typename Allocator> |
1116 | | const std::array<CharT,1> basic_json_encoder<CharT, Sink, Allocator>::comma = {','}; |
1117 | | template <typename CharT,typename Sink,typename Allocator> |
1118 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::comma_space = {',', ' '}; |
1119 | | template <typename CharT,typename Sink,typename Allocator> |
1120 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::space_comma = {' ', ','}; |
1121 | | template <typename CharT,typename Sink,typename Allocator> |
1122 | | const std::array<CharT,3> basic_json_encoder<CharT,Sink,Allocator>::space_comma_space = {' ', ',', ' '}; |
1123 | | |
1124 | | template <typename CharT, typename Sink, typename Allocator> |
1125 | | const std::array<CharT,1> basic_json_encoder<CharT, Sink, Allocator>::left_brace = {'{'}; |
1126 | | template <typename CharT,typename Sink,typename Allocator> |
1127 | | const std::array<CharT,1> basic_json_encoder<CharT,Sink,Allocator>::right_brace = {'}'}; |
1128 | | template <typename CharT,typename Sink,typename Allocator> |
1129 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::left_brace_space = {'{', ' '}; |
1130 | | template <typename CharT,typename Sink,typename Allocator> |
1131 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::space_right_brace = {' ', '}'}; |
1132 | | |
1133 | | template <typename CharT, typename Sink, typename Allocator> |
1134 | | const std::array<CharT,1> basic_json_encoder<CharT, Sink, Allocator>::left_bracket = {'['}; |
1135 | | template <typename CharT,typename Sink,typename Allocator> |
1136 | | const std::array<CharT,1> basic_json_encoder<CharT,Sink,Allocator>::right_bracket = {']'}; |
1137 | | template <typename CharT,typename Sink,typename Allocator> |
1138 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::left_bracket_space = {'[', ' '}; |
1139 | | template <typename CharT,typename Sink,typename Allocator> |
1140 | | const std::array<CharT,2> basic_json_encoder<CharT,Sink,Allocator>::space_right_bracket = {' ', ']'}; |
1141 | | |
1142 | | template <typename CharT,typename Sink=jsoncons::stream_sink<CharT>,typename Allocator=std::allocator<char>> |
1143 | | class basic_compact_json_encoder final : public basic_json_visitor<CharT> |
1144 | | { |
1145 | | static const std::array<CharT, 4>& null_constant() |
1146 | | { |
1147 | | static constexpr std::array<CharT,4> k{{'n','u','l','l'}}; |
1148 | | return k; |
1149 | | } |
1150 | | static const std::array<CharT, 4>& true_constant() |
1151 | | { |
1152 | | static constexpr std::array<CharT,4> k{{'t','r','u','e'}}; |
1153 | | return k; |
1154 | | } |
1155 | | static const std::array<CharT, 5>& false_constant() |
1156 | | { |
1157 | | static constexpr std::array<CharT,5> k{{'f','a','l','s','e'}}; |
1158 | | return k; |
1159 | | } |
1160 | | public: |
1161 | | using allocator_type = Allocator; |
1162 | | using char_type = CharT; |
1163 | | using typename basic_json_visitor<CharT>::string_view_type; |
1164 | | using sink_type = Sink; |
1165 | | using string_type = typename basic_json_encode_options<CharT>::string_type; |
1166 | | |
1167 | | private: |
1168 | | enum class container_type {object, array}; |
1169 | | |
1170 | | class encoding_context |
1171 | | { |
1172 | | container_type type_; |
1173 | | std::size_t count_{0}; |
1174 | | public: |
1175 | | encoding_context(container_type type) noexcept |
1176 | | : type_(type) |
1177 | | { |
1178 | | } |
1179 | | |
1180 | | std::size_t count() const |
1181 | | { |
1182 | | return count_; |
1183 | | } |
1184 | | |
1185 | | void increment_count() |
1186 | | { |
1187 | | ++count_; |
1188 | | } |
1189 | | |
1190 | | bool is_array() const |
1191 | | { |
1192 | | return type_ == container_type::array; |
1193 | | } |
1194 | | }; |
1195 | | using encoding_context_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context>; |
1196 | | |
1197 | | Sink sink_; |
1198 | | basic_json_encode_options<CharT> options_; |
1199 | | jsoncons::write_double fp_; |
1200 | | std::vector<encoding_context,encoding_context_allocator_type> stack_; |
1201 | | int nesting_depth_; |
1202 | | public: |
1203 | | |
1204 | | // Noncopyable and nonmoveable |
1205 | | basic_compact_json_encoder(const basic_compact_json_encoder&) = delete; |
1206 | | basic_compact_json_encoder(basic_compact_json_encoder&&) = delete; |
1207 | | |
1208 | | basic_compact_json_encoder(Sink&& sink, |
1209 | | const Allocator& alloc = Allocator()) |
1210 | | : basic_compact_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc) |
1211 | | { |
1212 | | } |
1213 | | |
1214 | | basic_compact_json_encoder(Sink&& sink, |
1215 | | const basic_json_encode_options<CharT>& options, |
1216 | | const Allocator& alloc = Allocator()) |
1217 | | : sink_(std::forward<Sink>(sink)), |
1218 | | options_(options), |
1219 | | fp_(options.float_format(), options.precision()), |
1220 | | stack_(alloc), |
1221 | | nesting_depth_(0) |
1222 | | { |
1223 | | } |
1224 | | |
1225 | | ~basic_compact_json_encoder() noexcept |
1226 | | { |
1227 | | JSONCONS_TRY |
1228 | | { |
1229 | | sink_.flush(); |
1230 | | } |
1231 | | JSONCONS_CATCH(...) |
1232 | | { |
1233 | | } |
1234 | | } |
1235 | | |
1236 | | basic_compact_json_encoder& operator=(const basic_compact_json_encoder&) = delete; |
1237 | | basic_compact_json_encoder& operator=(basic_compact_json_encoder&&) = delete; |
1238 | | |
1239 | | void reset() |
1240 | | { |
1241 | | stack_.clear(); |
1242 | | nesting_depth_ = 0; |
1243 | | } |
1244 | | |
1245 | | void reset(Sink&& sink) |
1246 | | { |
1247 | | sink_ = std::move(sink); |
1248 | | reset(); |
1249 | | } |
1250 | | |
1251 | | private: |
1252 | | // Implementing methods |
1253 | | void visit_flush() final |
1254 | | { |
1255 | | sink_.flush(); |
1256 | | } |
1257 | | |
1258 | | JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) final |
1259 | | { |
1260 | | if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) |
1261 | | { |
1262 | | ec = json_errc::max_nesting_depth_exceeded; |
1263 | | JSONCONS_VISITOR_RETURN; |
1264 | | } |
1265 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1266 | | { |
1267 | | sink_.push_back(','); |
1268 | | } |
1269 | | |
1270 | | stack_.emplace_back(container_type::object); |
1271 | | sink_.push_back('{'); |
1272 | | JSONCONS_VISITOR_RETURN; |
1273 | | } |
1274 | | |
1275 | | JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) final |
1276 | | { |
1277 | | JSONCONS_ASSERT(!stack_.empty()); |
1278 | | --nesting_depth_; |
1279 | | |
1280 | | stack_.pop_back(); |
1281 | | sink_.push_back('}'); |
1282 | | |
1283 | | if (!stack_.empty()) |
1284 | | { |
1285 | | stack_.back().increment_count(); |
1286 | | } |
1287 | | JSONCONS_VISITOR_RETURN; |
1288 | | } |
1289 | | |
1290 | | |
1291 | | JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) final |
1292 | | { |
1293 | | if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) |
1294 | | { |
1295 | | ec = json_errc::max_nesting_depth_exceeded; |
1296 | | JSONCONS_VISITOR_RETURN; |
1297 | | } |
1298 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1299 | | { |
1300 | | sink_.push_back(','); |
1301 | | } |
1302 | | stack_.emplace_back(container_type::array); |
1303 | | sink_.push_back('['); |
1304 | | JSONCONS_VISITOR_RETURN; |
1305 | | } |
1306 | | |
1307 | | JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) final |
1308 | | { |
1309 | | JSONCONS_ASSERT(!stack_.empty()); |
1310 | | --nesting_depth_; |
1311 | | |
1312 | | stack_.pop_back(); |
1313 | | sink_.push_back(']'); |
1314 | | if (!stack_.empty()) |
1315 | | { |
1316 | | stack_.back().increment_count(); |
1317 | | } |
1318 | | JSONCONS_VISITOR_RETURN; |
1319 | | } |
1320 | | |
1321 | | JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code&) final |
1322 | | { |
1323 | | if (!stack_.empty() && stack_.back().count() > 0) |
1324 | | { |
1325 | | sink_.push_back(','); |
1326 | | } |
1327 | | |
1328 | | sink_.push_back('\"'); |
1329 | | jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); |
1330 | | sink_.push_back('\"'); |
1331 | | sink_.push_back(':'); |
1332 | | JSONCONS_VISITOR_RETURN; |
1333 | | } |
1334 | | |
1335 | | JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) final |
1336 | | { |
1337 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1338 | | { |
1339 | | sink_.push_back(','); |
1340 | | } |
1341 | | |
1342 | | sink_.append(null_constant().data(), null_constant().size()); |
1343 | | |
1344 | | if (!stack_.empty()) |
1345 | | { |
1346 | | stack_.back().increment_count(); |
1347 | | } |
1348 | | JSONCONS_VISITOR_RETURN; |
1349 | | } |
1350 | | |
1351 | | void write_bignum_value(const string_view_type& sv) |
1352 | | { |
1353 | | switch (options_.bignum_format()) |
1354 | | { |
1355 | | case bignum_format_kind::raw: |
1356 | | { |
1357 | | sink_.append(sv.data(),sv.size()); |
1358 | | break; |
1359 | | } |
1360 | | case bignum_format_kind::base64: |
1361 | | { |
1362 | | bigint n(sv.data(), sv.length()); |
1363 | | bool is_neg = n < 0; |
1364 | | if (is_neg) |
1365 | | { |
1366 | | n = - n -1; |
1367 | | } |
1368 | | int signum; |
1369 | | std::vector<uint8_t> v; |
1370 | | n.write_bytes_be(signum, v); |
1371 | | |
1372 | | sink_.push_back('\"'); |
1373 | | if (is_neg) |
1374 | | { |
1375 | | sink_.push_back('~'); |
1376 | | } |
1377 | | bytes_to_base64(v.begin(), v.end(), sink_); |
1378 | | sink_.push_back('\"'); |
1379 | | break; |
1380 | | } |
1381 | | case bignum_format_kind::base64url: |
1382 | | { |
1383 | | bigint n(sv.data(), sv.length()); |
1384 | | bool is_neg = n < 0; |
1385 | | if (is_neg) |
1386 | | { |
1387 | | n = - n -1; |
1388 | | } |
1389 | | int signum; |
1390 | | std::vector<uint8_t> v; |
1391 | | n.write_bytes_be(signum, v); |
1392 | | |
1393 | | sink_.push_back('\"'); |
1394 | | if (is_neg) |
1395 | | { |
1396 | | sink_.push_back('~'); |
1397 | | } |
1398 | | bytes_to_base64url(v.begin(), v.end(), sink_); |
1399 | | sink_.push_back('\"'); |
1400 | | break; |
1401 | | } |
1402 | | default: |
1403 | | { |
1404 | | sink_.push_back('\"'); |
1405 | | sink_.append(sv.data(),sv.size()); |
1406 | | sink_.push_back('\"'); |
1407 | | break; |
1408 | | } |
1409 | | } |
1410 | | } |
1411 | | |
1412 | | JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) final |
1413 | | { |
1414 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1415 | | { |
1416 | | sink_.push_back(','); |
1417 | | } |
1418 | | |
1419 | | write_string(sv, tag, context, ec); |
1420 | | |
1421 | | if (!stack_.empty()) |
1422 | | { |
1423 | | stack_.back().increment_count(); |
1424 | | } |
1425 | | JSONCONS_VISITOR_RETURN; |
1426 | | } |
1427 | | |
1428 | | void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) |
1429 | | { |
1430 | | if (JSONCONS_LIKELY(tag == semantic_tag::noesc && !options_.escape_all_non_ascii() && !options_.escape_solidus())) |
1431 | | { |
1432 | | //std::cout << "noesc\n"; |
1433 | | sink_.push_back('\"'); |
1434 | | const CharT* begin = sv.data(); |
1435 | | const CharT* end = begin + sv.length(); |
1436 | | for (const CharT* it = begin; it != end; ++it) |
1437 | | { |
1438 | | sink_.push_back(*it); |
1439 | | } |
1440 | | sink_.push_back('\"'); |
1441 | | } |
1442 | | else if (tag == semantic_tag::bigint) |
1443 | | { |
1444 | | write_bignum_value(sv); |
1445 | | } |
1446 | | else if (tag == semantic_tag::bigdec && options_.bignum_format() == bignum_format_kind::raw) |
1447 | | { |
1448 | | write_bignum_value(sv); |
1449 | | } |
1450 | | else |
1451 | | { |
1452 | | //if (tag != semantic_tag::bigdec) |
1453 | | // std::cout << "esc\n"; |
1454 | | sink_.push_back('\"'); |
1455 | | jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); |
1456 | | sink_.push_back('\"'); |
1457 | | } |
1458 | | } |
1459 | | |
1460 | | JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, |
1461 | | semantic_tag tag, |
1462 | | const ser_context&, |
1463 | | std::error_code&) final |
1464 | | { |
1465 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1466 | | { |
1467 | | sink_.push_back(','); |
1468 | | } |
1469 | | |
1470 | | byte_string_chars_format encoding_hint; |
1471 | | switch (tag) |
1472 | | { |
1473 | | case semantic_tag::base16: |
1474 | | encoding_hint = byte_string_chars_format::base16; |
1475 | | break; |
1476 | | case semantic_tag::base64: |
1477 | | encoding_hint = byte_string_chars_format::base64; |
1478 | | break; |
1479 | | case semantic_tag::base64url: |
1480 | | encoding_hint = byte_string_chars_format::base64url; |
1481 | | break; |
1482 | | default: |
1483 | | encoding_hint = byte_string_chars_format::none; |
1484 | | break; |
1485 | | } |
1486 | | |
1487 | | byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), |
1488 | | encoding_hint, |
1489 | | byte_string_chars_format::base64url); |
1490 | | switch (format) |
1491 | | { |
1492 | | case byte_string_chars_format::base16: |
1493 | | { |
1494 | | sink_.push_back('\"'); |
1495 | | bytes_to_base16(b.begin(),b.end(),sink_); |
1496 | | sink_.push_back('\"'); |
1497 | | break; |
1498 | | } |
1499 | | case byte_string_chars_format::base64: |
1500 | | { |
1501 | | sink_.push_back('\"'); |
1502 | | bytes_to_base64(b.begin(), b.end(), sink_); |
1503 | | sink_.push_back('\"'); |
1504 | | break; |
1505 | | } |
1506 | | case byte_string_chars_format::base64url: |
1507 | | { |
1508 | | sink_.push_back('\"'); |
1509 | | bytes_to_base64url(b.begin(),b.end(),sink_); |
1510 | | sink_.push_back('\"'); |
1511 | | break; |
1512 | | } |
1513 | | default: |
1514 | | { |
1515 | | JSONCONS_UNREACHABLE(); |
1516 | | } |
1517 | | } |
1518 | | |
1519 | | if (!stack_.empty()) |
1520 | | { |
1521 | | stack_.back().increment_count(); |
1522 | | } |
1523 | | JSONCONS_VISITOR_RETURN; |
1524 | | } |
1525 | | |
1526 | | JSONCONS_VISITOR_RETURN_TYPE visit_double(double value, |
1527 | | semantic_tag, |
1528 | | const ser_context& context, |
1529 | | std::error_code& ec) final |
1530 | | { |
1531 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1532 | | { |
1533 | | sink_.push_back(','); |
1534 | | } |
1535 | | |
1536 | | if (JSONCONS_UNLIKELY(!std::isfinite(value))) |
1537 | | { |
1538 | | if ((std::isnan)(value)) |
1539 | | { |
1540 | | if (options_.enable_nan_to_num()) |
1541 | | { |
1542 | | sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length()); |
1543 | | } |
1544 | | else if (options_.enable_nan_to_str()) |
1545 | | { |
1546 | | write_string(options_.nan_to_str(), semantic_tag::none, context, ec); |
1547 | | } |
1548 | | else |
1549 | | { |
1550 | | sink_.append(null_constant().data(), null_constant().size()); |
1551 | | } |
1552 | | } |
1553 | | else if (value == std::numeric_limits<double>::infinity()) |
1554 | | { |
1555 | | if (options_.enable_inf_to_num()) |
1556 | | { |
1557 | | sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length()); |
1558 | | } |
1559 | | else if (options_.enable_inf_to_str()) |
1560 | | { |
1561 | | write_string(options_.inf_to_str(), semantic_tag::none, context, ec); |
1562 | | } |
1563 | | else |
1564 | | { |
1565 | | sink_.append(null_constant().data(), null_constant().size()); |
1566 | | } |
1567 | | } |
1568 | | else |
1569 | | { |
1570 | | if (options_.enable_neginf_to_num()) |
1571 | | { |
1572 | | sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length()); |
1573 | | } |
1574 | | else if (options_.enable_neginf_to_str()) |
1575 | | { |
1576 | | write_string(options_.neginf_to_str(), semantic_tag::none, context, ec); |
1577 | | } |
1578 | | else |
1579 | | { |
1580 | | sink_.append(null_constant().data(), null_constant().size()); |
1581 | | } |
1582 | | } |
1583 | | } |
1584 | | else |
1585 | | { |
1586 | | fp_(value, sink_); |
1587 | | } |
1588 | | |
1589 | | if (!stack_.empty()) |
1590 | | { |
1591 | | stack_.back().increment_count(); |
1592 | | } |
1593 | | JSONCONS_VISITOR_RETURN; |
1594 | | } |
1595 | | |
1596 | | JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t value, |
1597 | | semantic_tag, |
1598 | | const ser_context&, |
1599 | | std::error_code&) final |
1600 | | { |
1601 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1602 | | { |
1603 | | sink_.push_back(','); |
1604 | | } |
1605 | | jsoncons::from_integer(value, sink_); |
1606 | | if (!stack_.empty()) |
1607 | | { |
1608 | | stack_.back().increment_count(); |
1609 | | } |
1610 | | JSONCONS_VISITOR_RETURN; |
1611 | | } |
1612 | | |
1613 | | JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t value, |
1614 | | semantic_tag, |
1615 | | const ser_context&, |
1616 | | std::error_code&) final |
1617 | | { |
1618 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1619 | | { |
1620 | | sink_.push_back(','); |
1621 | | } |
1622 | | jsoncons::from_integer(value, sink_); |
1623 | | if (!stack_.empty()) |
1624 | | { |
1625 | | stack_.back().increment_count(); |
1626 | | } |
1627 | | JSONCONS_VISITOR_RETURN; |
1628 | | } |
1629 | | |
1630 | | JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) final |
1631 | | { |
1632 | | if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) |
1633 | | { |
1634 | | sink_.push_back(','); |
1635 | | } |
1636 | | |
1637 | | if (value) |
1638 | | { |
1639 | | sink_.append(true_constant().data(), true_constant().size()); |
1640 | | } |
1641 | | else |
1642 | | { |
1643 | | sink_.append(false_constant().data(), false_constant().size()); |
1644 | | } |
1645 | | |
1646 | | if (!stack_.empty()) |
1647 | | { |
1648 | | stack_.back().increment_count(); |
1649 | | } |
1650 | | JSONCONS_VISITOR_RETURN; |
1651 | | } |
1652 | | }; |
1653 | | |
1654 | | using json_stream_encoder = basic_json_encoder<char,jsoncons::stream_sink<char>>; |
1655 | | using wjson_stream_encoder = basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>; |
1656 | | using compact_json_stream_encoder = basic_compact_json_encoder<char,jsoncons::stream_sink<char>>; |
1657 | | using compact_wjson_stream_encoder = basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>; |
1658 | | |
1659 | | using json_string_encoder = basic_json_encoder<char,jsoncons::string_sink<std::string>>; |
1660 | | using wjson_string_encoder = basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>; |
1661 | | using compact_json_string_encoder = basic_compact_json_encoder<char,jsoncons::string_sink<std::string>>; |
1662 | | using compact_wjson_string_encoder = basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>; |
1663 | | |
1664 | | } // namespace jsoncons |
1665 | | |
1666 | | #endif // JSONCONS_JSON_ENCODER_HPP |