JsonCpp project page Classes Namespace JsonCpp home page

json_writer.cpp
Go to the documentation of this file.
1// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
2// Distributed under MIT license, or public domain if desired and
3// recognized in your jurisdiction.
4// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5
6#if !defined(JSON_IS_AMALGAMATION)
7#include "json_tool.h"
8#include <json/writer.h>
9#endif // if !defined(JSON_IS_AMALGAMATION)
10#include <algorithm>
11#include <cassert>
12#include <cctype>
13#include <cstring>
14#include <iomanip>
15#include <memory>
16#include <set>
17#include <sstream>
18#include <utility>
19
20#if __cplusplus >= 201103L
21#include <cmath>
22#include <cstdio>
23
24#if !defined(isnan)
25#define isnan std::isnan
26#endif
27
28#if !defined(isfinite)
29#define isfinite std::isfinite
30#endif
31
32#else
33#include <cmath>
34#include <cstdio>
35
36#if defined(_MSC_VER)
37#if !defined(isnan)
38#include <float.h>
39#define isnan _isnan
40#endif
41
42#if !defined(isfinite)
43#include <float.h>
44#define isfinite _finite
45#endif
46
47#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
48#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
49#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
50
51#endif //_MSC_VER
52
53#if defined(__sun) && defined(__SVR4) // Solaris
54#if !defined(isfinite)
55#include <ieeefp.h>
56#define isfinite finite
57#endif
58#endif
59
60#if defined(__hpux)
61#if !defined(isfinite)
62#if defined(__ia64) && !defined(finite)
63#define isfinite(x) \
64 ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
65#endif
66#endif
67#endif
68
69#if !defined(isnan)
70// IEEE standard states that NaN values will not compare to themselves
71#define isnan(x) ((x) != (x))
72#endif
73
74#if !defined(__APPLE__)
75#if !defined(isfinite)
76#define isfinite finite
77#endif
78#endif
79#endif
80
81#if defined(_MSC_VER)
82// Disable warning about strdup being deprecated.
83#pragma warning(disable : 4996)
84#endif
85
86namespace Json {
87
88#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
89using StreamWriterPtr = std::unique_ptr<StreamWriter>;
90#else
91using StreamWriterPtr = std::auto_ptr<StreamWriter>;
92#endif
93
95 UIntToStringBuffer buffer;
96 char* current = buffer + sizeof(buffer);
97 if (value == Value::minLargestInt) {
99 *--current = '-';
100 } else if (value < 0) {
101 uintToString(LargestUInt(-value), current);
102 *--current = '-';
103 } else {
104 uintToString(LargestUInt(value), current);
105 }
106 assert(current >= buffer);
107 return current;
108}
109
111 UIntToStringBuffer buffer;
112 char* current = buffer + sizeof(buffer);
113 uintToString(value, current);
114 assert(current >= buffer);
115 return current;
116}
117
118#if defined(JSON_HAS_INT64)
119
121
123
124#endif // # if defined(JSON_HAS_INT64)
125
126namespace {
127String valueToString(double value, bool useSpecialFloats,
128 unsigned int precision, PrecisionType precisionType) {
129 // Print into the buffer. We need not request the alternative representation
130 // that always has a decimal point because JSON doesn't distinguish the
131 // concepts of reals and integers.
132 if (!isfinite(value)) {
133 static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
134 {"null", "-1e+9999", "1e+9999"}};
135 return reps[useSpecialFloats ? 0 : 1][isnan(value) ? 0
136 : (value < 0) ? 1
137 : 2];
138 }
139
140 String buffer(size_t(36), '\0');
141 while (true) {
142 int len = jsoncpp_snprintf(
143 &*buffer.begin(), buffer.size(),
144 (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
145 precision, value);
146 assert(len >= 0);
147 auto wouldPrint = static_cast<size_t>(len);
148 if (wouldPrint >= buffer.size()) {
149 buffer.resize(wouldPrint + 1);
150 continue;
151 }
152 buffer.resize(wouldPrint);
153 break;
154 }
155
156 buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
157
158 // try to ensure we preserve the fact that this was given to us as a double on
159 // input
160 if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
161 buffer += ".0";
162 }
163
164 // strip the zero padding from the right
165 if (precisionType == PrecisionType::decimalPlaces) {
166 buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
167 buffer.end());
168 }
169
170 return buffer;
171}
172} // namespace
173
174String valueToString(double value, unsigned int precision,
175 PrecisionType precisionType) {
176 return valueToString(value, false, precision, precisionType);
177}
178
179String valueToString(bool value) { return value ? "true" : "false"; }
180
181static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
182 assert(s || !n);
183
184 return std::any_of(s, s + n, [](unsigned char c) {
185 return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
186 });
187}
188
189static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
190 const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
191
192 unsigned int firstByte = static_cast<unsigned char>(*s);
193
194 if (firstByte < 0x80)
195 return firstByte;
196
197 if (firstByte < 0xE0) {
198 if (e - s < 2)
199 return REPLACEMENT_CHARACTER;
200
201 unsigned int calculated =
202 ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
203 s += 1;
204 // oversized encoded characters are invalid
205 return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
206 }
207
208 if (firstByte < 0xF0) {
209 if (e - s < 3)
210 return REPLACEMENT_CHARACTER;
211
212 unsigned int calculated = ((firstByte & 0x0F) << 12) |
213 ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
214 (static_cast<unsigned int>(s[2]) & 0x3F);
215 s += 2;
216 // surrogates aren't valid codepoints itself
217 // shouldn't be UTF-8 encoded
218 if (calculated >= 0xD800 && calculated <= 0xDFFF)
219 return REPLACEMENT_CHARACTER;
220 // oversized encoded characters are invalid
221 return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
222 }
223
224 if (firstByte < 0xF8) {
225 if (e - s < 4)
226 return REPLACEMENT_CHARACTER;
227
228 unsigned int calculated = ((firstByte & 0x07) << 18) |
229 ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
230 ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
231 (static_cast<unsigned int>(s[3]) & 0x3F);
232 s += 3;
233 // oversized encoded characters are invalid
234 return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
235 }
236
237 return REPLACEMENT_CHARACTER;
238}
239
240static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
241 "101112131415161718191a1b1c1d1e1f"
242 "202122232425262728292a2b2c2d2e2f"
243 "303132333435363738393a3b3c3d3e3f"
244 "404142434445464748494a4b4c4d4e4f"
245 "505152535455565758595a5b5c5d5e5f"
246 "606162636465666768696a6b6c6d6e6f"
247 "707172737475767778797a7b7c7d7e7f"
248 "808182838485868788898a8b8c8d8e8f"
249 "909192939495969798999a9b9c9d9e9f"
250 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
251 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
252 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
253 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
254 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
255 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
256
257static String toHex16Bit(unsigned int x) {
258 const unsigned int hi = (x >> 8) & 0xff;
259 const unsigned int lo = x & 0xff;
260 String result(4, ' ');
261 result[0] = hex2[2 * hi];
262 result[1] = hex2[2 * hi + 1];
263 result[2] = hex2[2 * lo];
264 result[3] = hex2[2 * lo + 1];
265 return result;
266}
267
268static void appendRaw(String& result, unsigned ch) {
269 result += static_cast<char>(ch);
270}
271
272static void appendHex(String& result, unsigned ch) {
273 result.append("\\u").append(toHex16Bit(ch));
274}
275
276static String valueToQuotedStringN(const char* value, size_t length,
277 bool emitUTF8 = false) {
278 if (value == nullptr)
279 return "";
280
281 if (!doesAnyCharRequireEscaping(value, length))
282 return String("\"") + value + "\"";
283 // We have to walk value and escape any special characters.
284 // Appending to String is not efficient, but this should be rare.
285 // (Note: forward slashes are *not* rare, but I am not escaping them.)
286 String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
287 String result;
288 result.reserve(maxsize); // to avoid lots of mallocs
289 result += "\"";
290 char const* end = value + length;
291 for (const char* c = value; c != end; ++c) {
292 switch (*c) {
293 case '\"':
294 result += "\\\"";
295 break;
296 case '\\':
297 result += "\\\\";
298 break;
299 case '\b':
300 result += "\\b";
301 break;
302 case '\f':
303 result += "\\f";
304 break;
305 case '\n':
306 result += "\\n";
307 break;
308 case '\r':
309 result += "\\r";
310 break;
311 case '\t':
312 result += "\\t";
313 break;
314 // case '/':
315 // Even though \/ is considered a legal escape in JSON, a bare
316 // slash is also legal, so I see no reason to escape it.
317 // (I hope I am not misunderstanding something.)
318 // blep notes: actually escaping \/ may be useful in javascript to avoid </
319 // sequence.
320 // Should add a flag to allow this compatibility mode and prevent this
321 // sequence from occurring.
322 default: {
323 if (emitUTF8) {
324 unsigned codepoint = static_cast<unsigned char>(*c);
325 if (codepoint < 0x20) {
326 appendHex(result, codepoint);
327 } else {
328 appendRaw(result, codepoint);
329 }
330 } else {
331 unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
332 if (codepoint < 0x20) {
333 appendHex(result, codepoint);
334 } else if (codepoint < 0x80) {
335 appendRaw(result, codepoint);
336 } else if (codepoint < 0x10000) {
337 // Basic Multilingual Plane
338 appendHex(result, codepoint);
339 } else {
340 // Extended Unicode. Encode 20 bits as a surrogate pair.
341 codepoint -= 0x10000;
342 appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
343 appendHex(result, 0xdc00 + (codepoint & 0x3ff));
344 }
345 }
346 } break;
347 }
348 }
349 result += "\"";
350 return result;
351}
352
353String valueToQuotedString(const char* value) {
354 return valueToQuotedStringN(value, strlen(value));
355}
356
357String valueToQuotedString(const char* value, size_t length) {
358 return valueToQuotedStringN(value, length);
359}
360
361// Class Writer
362// //////////////////////////////////////////////////////////////////
363Writer::~Writer() = default;
364
365// Class FastWriter
366// //////////////////////////////////////////////////////////////////
367
369
370 = default;
371
372void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
373
374void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
375
376void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
377
379 document_.clear();
380 writeValue(root);
381 if (!omitEndingLineFeed_)
382 document_ += '\n';
383 return document_;
384}
385
386void FastWriter::writeValue(const Value& value) {
387 switch (value.type()) {
388 case nullValue:
389 if (!dropNullPlaceholders_)
390 document_ += "null";
391 break;
392 case intValue:
393 document_ += valueToString(value.asLargestInt());
394 break;
395 case uintValue:
396 document_ += valueToString(value.asLargestUInt());
397 break;
398 case realValue:
399 document_ += valueToString(value.asDouble());
400 break;
401 case stringValue: {
402 // Is NULL possible for value.string_? No.
403 char const* str;
404 char const* end;
405 bool ok = value.getString(&str, &end);
406 if (ok)
407 document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
408 break;
409 }
410 case booleanValue:
411 document_ += valueToString(value.asBool());
412 break;
413 case arrayValue: {
414 document_ += '[';
415 ArrayIndex size = value.size();
416 for (ArrayIndex index = 0; index < size; ++index) {
417 if (index > 0)
418 document_ += ',';
419 writeValue(value[index]);
420 }
421 document_ += ']';
422 } break;
423 case objectValue: {
424 Value::Members members(value.getMemberNames());
425 document_ += '{';
426 for (auto it = members.begin(); it != members.end(); ++it) {
427 const String& name = *it;
428 if (it != members.begin())
429 document_ += ',';
430 document_ += valueToQuotedStringN(name.data(), name.length());
431 document_ += yamlCompatibilityEnabled_ ? ": " : ":";
432 writeValue(value[name]);
433 }
434 document_ += '}';
435 } break;
436 }
437}
438
439// Class StyledWriter
440// //////////////////////////////////////////////////////////////////
441
443
445 document_.clear();
446 addChildValues_ = false;
447 indentString_.clear();
448 writeCommentBeforeValue(root);
449 writeValue(root);
450 writeCommentAfterValueOnSameLine(root);
451 document_ += '\n';
452 return document_;
453}
454
455void StyledWriter::writeValue(const Value& value) {
456 switch (value.type()) {
457 case nullValue:
458 pushValue("null");
459 break;
460 case intValue:
461 pushValue(valueToString(value.asLargestInt()));
462 break;
463 case uintValue:
464 pushValue(valueToString(value.asLargestUInt()));
465 break;
466 case realValue:
467 pushValue(valueToString(value.asDouble()));
468 break;
469 case stringValue: {
470 // Is NULL possible for value.string_? No.
471 char const* str;
472 char const* end;
473 bool ok = value.getString(&str, &end);
474 if (ok)
475 pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
476 else
477 pushValue("");
478 break;
479 }
480 case booleanValue:
481 pushValue(valueToString(value.asBool()));
482 break;
483 case arrayValue:
484 writeArrayValue(value);
485 break;
486 case objectValue: {
487 Value::Members members(value.getMemberNames());
488 if (members.empty())
489 pushValue("{}");
490 else {
491 writeWithIndent("{");
492 indent();
493 auto it = members.begin();
494 for (;;) {
495 const String& name = *it;
496 const Value& childValue = value[name];
497 writeCommentBeforeValue(childValue);
498 writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
499 document_ += " : ";
500 writeValue(childValue);
501 if (++it == members.end()) {
502 writeCommentAfterValueOnSameLine(childValue);
503 break;
504 }
505 document_ += ',';
506 writeCommentAfterValueOnSameLine(childValue);
507 }
508 unindent();
509 writeWithIndent("}");
510 }
511 } break;
512 }
513}
514
515void StyledWriter::writeArrayValue(const Value& value) {
516 size_t size = value.size();
517 if (size == 0)
518 pushValue("[]");
519 else {
520 bool isArrayMultiLine = isMultilineArray(value);
521 if (isArrayMultiLine) {
522 writeWithIndent("[");
523 indent();
524 bool hasChildValue = !childValues_.empty();
525 ArrayIndex index = 0;
526 for (;;) {
527 const Value& childValue = value[index];
528 writeCommentBeforeValue(childValue);
529 if (hasChildValue)
530 writeWithIndent(childValues_[index]);
531 else {
532 writeIndent();
533 writeValue(childValue);
534 }
535 if (++index == size) {
536 writeCommentAfterValueOnSameLine(childValue);
537 break;
538 }
539 document_ += ',';
540 writeCommentAfterValueOnSameLine(childValue);
541 }
542 unindent();
543 writeWithIndent("]");
544 } else // output on a single line
545 {
546 assert(childValues_.size() == size);
547 document_ += "[ ";
548 for (size_t index = 0; index < size; ++index) {
549 if (index > 0)
550 document_ += ", ";
551 document_ += childValues_[index];
552 }
553 document_ += " ]";
554 }
555 }
556}
557
558bool StyledWriter::isMultilineArray(const Value& value) {
559 ArrayIndex const size = value.size();
560 bool isMultiLine = size * 3 >= rightMargin_;
561 childValues_.clear();
562 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
563 const Value& childValue = value[index];
564 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
565 !childValue.empty());
566 }
567 if (!isMultiLine) // check if line length > max line length
568 {
569 childValues_.reserve(size);
570 addChildValues_ = true;
571 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
572 for (ArrayIndex index = 0; index < size; ++index) {
573 if (hasCommentForValue(value[index])) {
574 isMultiLine = true;
575 }
576 writeValue(value[index]);
577 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
578 }
579 addChildValues_ = false;
580 isMultiLine = isMultiLine || lineLength >= rightMargin_;
581 }
582 return isMultiLine;
583}
584
585void StyledWriter::pushValue(const String& value) {
586 if (addChildValues_)
587 childValues_.push_back(value);
588 else
589 document_ += value;
590}
591
592void StyledWriter::writeIndent() {
593 if (!document_.empty()) {
594 char last = document_[document_.length() - 1];
595 if (last == ' ') // already indented
596 return;
597 if (last != '\n') // Comments may add new-line
598 document_ += '\n';
599 }
600 document_ += indentString_;
601}
602
603void StyledWriter::writeWithIndent(const String& value) {
604 writeIndent();
605 document_ += value;
606}
607
608void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
609
610void StyledWriter::unindent() {
611 assert(indentString_.size() >= indentSize_);
612 indentString_.resize(indentString_.size() - indentSize_);
613}
614
615void StyledWriter::writeCommentBeforeValue(const Value& root) {
616 if (!root.hasComment(commentBefore))
617 return;
618
619 document_ += '\n';
620 writeIndent();
621 const String& comment = root.getComment(commentBefore);
622 String::const_iterator iter = comment.begin();
623 while (iter != comment.end()) {
624 document_ += *iter;
625 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
626 writeIndent();
627 ++iter;
628 }
629
630 // Comments are stripped of trailing newlines, so add one here
631 document_ += '\n';
632}
633
634void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
635 if (root.hasComment(commentAfterOnSameLine))
636 document_ += " " + root.getComment(commentAfterOnSameLine);
637
638 if (root.hasComment(commentAfter)) {
639 document_ += '\n';
640 document_ += root.getComment(commentAfter);
641 document_ += '\n';
642 }
643}
644
645bool StyledWriter::hasCommentForValue(const Value& value) {
646 return value.hasComment(commentBefore) ||
647 value.hasComment(commentAfterOnSameLine) ||
648 value.hasComment(commentAfter);
649}
650
651// Class StyledStreamWriter
652// //////////////////////////////////////////////////////////////////
653
655 : document_(nullptr), indentation_(std::move(indentation)),
656 addChildValues_(), indented_(false) {}
657
658void StyledStreamWriter::write(OStream& out, const Value& root) {
659 document_ = &out;
660 addChildValues_ = false;
661 indentString_.clear();
662 indented_ = true;
663 writeCommentBeforeValue(root);
664 if (!indented_)
665 writeIndent();
666 indented_ = true;
667 writeValue(root);
668 writeCommentAfterValueOnSameLine(root);
669 *document_ << "\n";
670 document_ = nullptr; // Forget the stream, for safety.
671}
672
673void StyledStreamWriter::writeValue(const Value& value) {
674 switch (value.type()) {
675 case nullValue:
676 pushValue("null");
677 break;
678 case intValue:
679 pushValue(valueToString(value.asLargestInt()));
680 break;
681 case uintValue:
682 pushValue(valueToString(value.asLargestUInt()));
683 break;
684 case realValue:
685 pushValue(valueToString(value.asDouble()));
686 break;
687 case stringValue: {
688 // Is NULL possible for value.string_? No.
689 char const* str;
690 char const* end;
691 bool ok = value.getString(&str, &end);
692 if (ok)
693 pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
694 else
695 pushValue("");
696 break;
697 }
698 case booleanValue:
699 pushValue(valueToString(value.asBool()));
700 break;
701 case arrayValue:
702 writeArrayValue(value);
703 break;
704 case objectValue: {
705 Value::Members members(value.getMemberNames());
706 if (members.empty())
707 pushValue("{}");
708 else {
709 writeWithIndent("{");
710 indent();
711 auto it = members.begin();
712 for (;;) {
713 const String& name = *it;
714 const Value& childValue = value[name];
715 writeCommentBeforeValue(childValue);
716 writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
717 *document_ << " : ";
718 writeValue(childValue);
719 if (++it == members.end()) {
720 writeCommentAfterValueOnSameLine(childValue);
721 break;
722 }
723 *document_ << ",";
724 writeCommentAfterValueOnSameLine(childValue);
725 }
726 unindent();
727 writeWithIndent("}");
728 }
729 } break;
730 }
731}
732
733void StyledStreamWriter::writeArrayValue(const Value& value) {
734 unsigned size = value.size();
735 if (size == 0)
736 pushValue("[]");
737 else {
738 bool isArrayMultiLine = isMultilineArray(value);
739 if (isArrayMultiLine) {
740 writeWithIndent("[");
741 indent();
742 bool hasChildValue = !childValues_.empty();
743 unsigned index = 0;
744 for (;;) {
745 const Value& childValue = value[index];
746 writeCommentBeforeValue(childValue);
747 if (hasChildValue)
748 writeWithIndent(childValues_[index]);
749 else {
750 if (!indented_)
751 writeIndent();
752 indented_ = true;
753 writeValue(childValue);
754 indented_ = false;
755 }
756 if (++index == size) {
757 writeCommentAfterValueOnSameLine(childValue);
758 break;
759 }
760 *document_ << ",";
761 writeCommentAfterValueOnSameLine(childValue);
762 }
763 unindent();
764 writeWithIndent("]");
765 } else // output on a single line
766 {
767 assert(childValues_.size() == size);
768 *document_ << "[ ";
769 for (unsigned index = 0; index < size; ++index) {
770 if (index > 0)
771 *document_ << ", ";
772 *document_ << childValues_[index];
773 }
774 *document_ << " ]";
775 }
776 }
777}
778
779bool StyledStreamWriter::isMultilineArray(const Value& value) {
780 ArrayIndex const size = value.size();
781 bool isMultiLine = size * 3 >= rightMargin_;
782 childValues_.clear();
783 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
784 const Value& childValue = value[index];
785 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
786 !childValue.empty());
787 }
788 if (!isMultiLine) // check if line length > max line length
789 {
790 childValues_.reserve(size);
791 addChildValues_ = true;
792 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
793 for (ArrayIndex index = 0; index < size; ++index) {
794 if (hasCommentForValue(value[index])) {
795 isMultiLine = true;
796 }
797 writeValue(value[index]);
798 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
799 }
800 addChildValues_ = false;
801 isMultiLine = isMultiLine || lineLength >= rightMargin_;
802 }
803 return isMultiLine;
804}
805
806void StyledStreamWriter::pushValue(const String& value) {
807 if (addChildValues_)
808 childValues_.push_back(value);
809 else
810 *document_ << value;
811}
812
813void StyledStreamWriter::writeIndent() {
814 // blep intended this to look at the so-far-written string
815 // to determine whether we are already indented, but
816 // with a stream we cannot do that. So we rely on some saved state.
817 // The caller checks indented_.
818 *document_ << '\n' << indentString_;
819}
820
821void StyledStreamWriter::writeWithIndent(const String& value) {
822 if (!indented_)
823 writeIndent();
824 *document_ << value;
825 indented_ = false;
826}
827
828void StyledStreamWriter::indent() { indentString_ += indentation_; }
829
830void StyledStreamWriter::unindent() {
831 assert(indentString_.size() >= indentation_.size());
832 indentString_.resize(indentString_.size() - indentation_.size());
833}
834
835void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
836 if (!root.hasComment(commentBefore))
837 return;
838
839 if (!indented_)
840 writeIndent();
841 const String& comment = root.getComment(commentBefore);
842 String::const_iterator iter = comment.begin();
843 while (iter != comment.end()) {
844 *document_ << *iter;
845 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
846 // writeIndent(); // would include newline
847 *document_ << indentString_;
848 ++iter;
849 }
850 indented_ = false;
851}
852
853void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
854 if (root.hasComment(commentAfterOnSameLine))
855 *document_ << ' ' << root.getComment(commentAfterOnSameLine);
856
857 if (root.hasComment(commentAfter)) {
858 writeIndent();
859 *document_ << root.getComment(commentAfter);
860 }
861 indented_ = false;
862}
863
864bool StyledStreamWriter::hasCommentForValue(const Value& value) {
865 return value.hasComment(commentBefore) ||
866 value.hasComment(commentAfterOnSameLine) ||
867 value.hasComment(commentAfter);
868}
869
871// BuiltStyledStreamWriter
872
874struct CommentStyle {
876 enum Enum {
877 None,
878 Most,
879 All
880 };
881};
882
883struct BuiltStyledStreamWriter : public StreamWriter {
884 BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
885 String colonSymbol, String nullSymbol,
886 String endingLineFeedSymbol, bool useSpecialFloats,
887 bool emitUTF8, unsigned int precision,
888 PrecisionType precisionType);
889 int write(Value const& root, OStream* sout) override;
890
891private:
892 void writeValue(Value const& value);
893 void writeArrayValue(Value const& value);
894 bool isMultilineArray(Value const& value);
895 void pushValue(String const& value);
896 void writeIndent();
897 void writeWithIndent(String const& value);
898 void indent();
899 void unindent();
900 void writeCommentBeforeValue(Value const& root);
901 void writeCommentAfterValueOnSameLine(Value const& root);
902 static bool hasCommentForValue(const Value& value);
903
904 using ChildValues = std::vector<String>;
905
906 ChildValues childValues_;
907 String indentString_;
908 unsigned int rightMargin_;
909 String indentation_;
910 CommentStyle::Enum cs_;
911 String colonSymbol_;
912 String nullSymbol_;
913 String endingLineFeedSymbol_;
914 bool addChildValues_ : 1;
915 bool indented_ : 1;
916 bool useSpecialFloats_ : 1;
917 bool emitUTF8_ : 1;
918 unsigned int precision_;
919 PrecisionType precisionType_;
920};
921BuiltStyledStreamWriter::BuiltStyledStreamWriter(
922 String indentation, CommentStyle::Enum cs, String colonSymbol,
923 String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
924 bool emitUTF8, unsigned int precision, PrecisionType precisionType)
925 : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
926 colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
927 endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
928 addChildValues_(false), indented_(false),
929 useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
930 precision_(precision), precisionType_(precisionType) {}
931int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
932 sout_ = sout;
933 addChildValues_ = false;
934 indented_ = true;
935 indentString_.clear();
936 writeCommentBeforeValue(root);
937 if (!indented_)
938 writeIndent();
939 indented_ = true;
940 writeValue(root);
941 writeCommentAfterValueOnSameLine(root);
942 *sout_ << endingLineFeedSymbol_;
943 sout_ = nullptr;
944 return 0;
945}
946void BuiltStyledStreamWriter::writeValue(Value const& value) {
947 switch (value.type()) {
948 case nullValue:
949 pushValue(nullSymbol_);
950 break;
951 case intValue:
952 pushValue(valueToString(value.asLargestInt()));
953 break;
954 case uintValue:
955 pushValue(valueToString(value.asLargestUInt()));
956 break;
957 case realValue:
958 pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
959 precisionType_));
960 break;
961 case stringValue: {
962 // Is NULL is possible for value.string_? No.
963 char const* str;
964 char const* end;
965 bool ok = value.getString(&str, &end);
966 if (ok)
967 pushValue(
968 valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
969 else
970 pushValue("");
971 break;
972 }
973 case booleanValue:
974 pushValue(valueToString(value.asBool()));
975 break;
976 case arrayValue:
977 writeArrayValue(value);
978 break;
979 case objectValue: {
980 Value::Members members(value.getMemberNames());
981 if (members.empty())
982 pushValue("{}");
983 else {
984 writeWithIndent("{");
985 indent();
986 auto it = members.begin();
987 for (;;) {
988 String const& name = *it;
989 Value const& childValue = value[name];
990 writeCommentBeforeValue(childValue);
991 writeWithIndent(
992 valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
993 *sout_ << colonSymbol_;
994 writeValue(childValue);
995 if (++it == members.end()) {
996 writeCommentAfterValueOnSameLine(childValue);
997 break;
998 }
999 *sout_ << ",";
1000 writeCommentAfterValueOnSameLine(childValue);
1001 }
1002 unindent();
1003 writeWithIndent("}");
1004 }
1005 } break;
1006 }
1007}
1008
1009void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
1010 unsigned size = value.size();
1011 if (size == 0)
1012 pushValue("[]");
1013 else {
1014 bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
1015 if (isMultiLine) {
1016 writeWithIndent("[");
1017 indent();
1018 bool hasChildValue = !childValues_.empty();
1019 unsigned index = 0;
1020 for (;;) {
1021 Value const& childValue = value[index];
1022 writeCommentBeforeValue(childValue);
1023 if (hasChildValue)
1024 writeWithIndent(childValues_[index]);
1025 else {
1026 if (!indented_)
1027 writeIndent();
1028 indented_ = true;
1029 writeValue(childValue);
1030 indented_ = false;
1031 }
1032 if (++index == size) {
1033 writeCommentAfterValueOnSameLine(childValue);
1034 break;
1035 }
1036 *sout_ << ",";
1037 writeCommentAfterValueOnSameLine(childValue);
1038 }
1039 unindent();
1040 writeWithIndent("]");
1041 } else // output on a single line
1042 {
1043 assert(childValues_.size() == size);
1044 *sout_ << "[";
1045 if (!indentation_.empty())
1046 *sout_ << " ";
1047 for (unsigned index = 0; index < size; ++index) {
1048 if (index > 0)
1049 *sout_ << ((!indentation_.empty()) ? ", " : ",");
1050 *sout_ << childValues_[index];
1051 }
1052 if (!indentation_.empty())
1053 *sout_ << " ";
1054 *sout_ << "]";
1055 }
1056 }
1057}
1058
1059bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
1060 ArrayIndex const size = value.size();
1061 bool isMultiLine = size * 3 >= rightMargin_;
1062 childValues_.clear();
1063 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1064 Value const& childValue = value[index];
1065 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1066 !childValue.empty());
1067 }
1068 if (!isMultiLine) // check if line length > max line length
1069 {
1070 childValues_.reserve(size);
1071 addChildValues_ = true;
1072 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1073 for (ArrayIndex index = 0; index < size; ++index) {
1074 if (hasCommentForValue(value[index])) {
1075 isMultiLine = true;
1076 }
1077 writeValue(value[index]);
1078 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1079 }
1080 addChildValues_ = false;
1081 isMultiLine = isMultiLine || lineLength >= rightMargin_;
1082 }
1083 return isMultiLine;
1084}
1085
1086void BuiltStyledStreamWriter::pushValue(String const& value) {
1087 if (addChildValues_)
1088 childValues_.push_back(value);
1089 else
1090 *sout_ << value;
1091}
1092
1093void BuiltStyledStreamWriter::writeIndent() {
1094 // blep intended this to look at the so-far-written string
1095 // to determine whether we are already indented, but
1096 // with a stream we cannot do that. So we rely on some saved state.
1097 // The caller checks indented_.
1098
1099 if (!indentation_.empty()) {
1100 // In this case, drop newlines too.
1101 *sout_ << '\n' << indentString_;
1102 }
1103}
1104
1105void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1106 if (!indented_)
1107 writeIndent();
1108 *sout_ << value;
1109 indented_ = false;
1110}
1111
1112void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1113
1114void BuiltStyledStreamWriter::unindent() {
1115 assert(indentString_.size() >= indentation_.size());
1116 indentString_.resize(indentString_.size() - indentation_.size());
1117}
1118
1119void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1120 if (cs_ == CommentStyle::None)
1121 return;
1122 if (!root.hasComment(commentBefore))
1123 return;
1124
1125 if (!indented_)
1126 writeIndent();
1127 const String& comment = root.getComment(commentBefore);
1128 String::const_iterator iter = comment.begin();
1129 while (iter != comment.end()) {
1130 *sout_ << *iter;
1131 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
1132 // writeIndent(); // would write extra newline
1133 *sout_ << indentString_;
1134 ++iter;
1135 }
1136 indented_ = false;
1137}
1138
1139void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
1140 Value const& root) {
1141 if (cs_ == CommentStyle::None)
1142 return;
1143 if (root.hasComment(commentAfterOnSameLine))
1144 *sout_ << " " + root.getComment(commentAfterOnSameLine);
1145
1146 if (root.hasComment(commentAfter)) {
1147 writeIndent();
1148 *sout_ << root.getComment(commentAfter);
1149 }
1150}
1151
1152// static
1153bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1154 return value.hasComment(commentBefore) ||
1155 value.hasComment(commentAfterOnSameLine) ||
1156 value.hasComment(commentAfter);
1157}
1158
1160// StreamWriter
1161
1162StreamWriter::StreamWriter() : sout_(nullptr) {}
1163StreamWriter::~StreamWriter() = default;
1168 const String indentation = settings_["indentation"].asString();
1169 const String cs_str = settings_["commentStyle"].asString();
1170 const String pt_str = settings_["precisionType"].asString();
1171 const bool eyc = settings_["enableYAMLCompatibility"].asBool();
1172 const bool dnp = settings_["dropNullPlaceholders"].asBool();
1173 const bool usf = settings_["useSpecialFloats"].asBool();
1174 const bool emitUTF8 = settings_["emitUTF8"].asBool();
1175 unsigned int pre = settings_["precision"].asUInt();
1176 CommentStyle::Enum cs = CommentStyle::All;
1177 if (cs_str == "All") {
1178 cs = CommentStyle::All;
1179 } else if (cs_str == "None") {
1180 cs = CommentStyle::None;
1181 } else {
1182 throwRuntimeError("commentStyle must be 'All' or 'None'");
1183 }
1184 PrecisionType precisionType(significantDigits);
1185 if (pt_str == "significant") {
1186 precisionType = PrecisionType::significantDigits;
1187 } else if (pt_str == "decimal") {
1188 precisionType = PrecisionType::decimalPlaces;
1189 } else {
1190 throwRuntimeError("precisionType must be 'significant' or 'decimal'");
1191 }
1192 String colonSymbol = " : ";
1193 if (eyc) {
1194 colonSymbol = ": ";
1195 } else if (indentation.empty()) {
1196 colonSymbol = ":";
1197 }
1198 String nullSymbol = "null";
1199 if (dnp) {
1200 nullSymbol.clear();
1201 }
1202 if (pre > 17)
1203 pre = 17;
1204 String endingLineFeedSymbol;
1205 return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1206 endingLineFeedSymbol, usf, emitUTF8, pre,
1207 precisionType);
1208}
1209
1211 static const auto& valid_keys = *new std::set<String>{
1212 "indentation",
1213 "commentStyle",
1214 "enableYAMLCompatibility",
1215 "dropNullPlaceholders",
1216 "useSpecialFloats",
1217 "emitUTF8",
1218 "precision",
1219 "precisionType",
1220 };
1221 for (auto si = settings_.begin(); si != settings_.end(); ++si) {
1222 auto key = si.name();
1223 if (valid_keys.count(key))
1224 continue;
1225 if (invalid)
1226 (*invalid)[key] = *si;
1227 else
1228 return false;
1229 }
1230 return invalid ? invalid->empty() : true;
1231}
1232
1234 return settings_[key];
1235}
1236// static
1239 (*settings)["commentStyle"] = "All";
1240 (*settings)["indentation"] = "\t";
1241 (*settings)["enableYAMLCompatibility"] = false;
1242 (*settings)["dropNullPlaceholders"] = false;
1243 (*settings)["useSpecialFloats"] = false;
1244 (*settings)["emitUTF8"] = false;
1245 (*settings)["precision"] = 17;
1246 (*settings)["precisionType"] = "significant";
1248}
1249
1250String writeString(StreamWriter::Factory const& factory, Value const& root) {
1251 OStringStream sout;
1252 StreamWriterPtr const writer(factory.newStreamWriter());
1253 writer->write(root, &sout);
1254 return std::move(sout).str();
1255}
1256
1257OStream& operator<<(OStream& sout, Value const& root) {
1258 StreamWriterBuilder builder;
1259 StreamWriterPtr const writer(builder.newStreamWriter());
1260 writer->write(root, &sout);
1261 return sout;
1262}
1263
1264} // namespace Json
String write(const Value &root) override
void dropNullPlaceholders()
Drop the "null" string from the writer's output for nullValues.
void enableYAMLCompatibility()
A simple abstract factory.
Definition writer.h:59
virtual StreamWriter * newStreamWriter() const =0
Allocate a CharReader via operator new().
Build a StreamWriter implementation.
Definition writer.h:90
StreamWriter * newStreamWriter() const override
bool validate(Json::Value *invalid) const
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
Json::Value settings_
Configuration of this builder.
Definition writer.h:122
Value & operator[](const String &key)
A simple way to update a specific setting.
virtual ~StreamWriter()
OStream * sout_
Definition writer.h:44
void write(OStream &out, const Value &root)
Serialize a Value in JSON format.
StyledStreamWriter(String indentation="\t")
String write(const Value &root) override
Serialize a Value in JSON format.
Represents a JSON value.
Definition value.h:194
const_iterator begin() const
bool empty() const
Return true if empty array, empty object, or null; otherwise, false.
static constexpr LargestInt maxLargestInt
Maximum signed integer value that can be stored in a Json::Value.
Definition value.h:227
ArrayIndex size() const
Number of values in array or object.
bool getString(char const **begin, char const **end) const
Get raw char* of string-value.
std::vector< String > Members
Definition value.h:198
const_iterator end() const
String asString() const
Embedded zeroes are possible.
UInt asUInt() const
Members getMemberNames() const
Return a list of the member names.
ValueType type() const
LargestInt asLargestInt() const
bool asBool() const
LargestUInt asLargestUInt() const
static constexpr LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
Definition value.h:224
double asDouble() const
virtual ~Writer()
#define jsoncpp_snprintf
Definition config.h:63
#define isnan
#define isfinite
JSON (JavaScript Object Notation).
Definition allocator.h:15
std::basic_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
Definition config.h:136
static const char hex2[]
static unsigned int utf8ToCodepoint(const char *&s, const char *e)
unsigned int ArrayIndex
Definition forwards.h:32
static void appendRaw(String &result, unsigned ch)
Int64 LargestInt
Definition config.h:123
String writeString(StreamWriter::Factory const &factory, Value const &root)
Write into stringstream, then return string, for convenience.
Iter fixNumericLocale(Iter begin, Iter end)
Change ',' to '.
Definition json_tool.h:94
@ commentAfterOnSameLine
a comment just after a value on the same line
Definition value.h:121
@ commentBefore
a comment placed on the line before a value
Definition value.h:120
@ commentAfter
a comment on the line after a value (only make sense for
Definition value.h:122
static String toHex16Bit(unsigned int x)
static bool doesAnyCharRequireEscaping(char const *s, size_t n)
String valueToQuotedString(const char *value)
static String valueToQuotedStringN(const char *value, size_t length, bool emitUTF8=false)
String valueToString(Int value)
unsigned int UInt
Definition config.h:109
@ booleanValue
bool value
Definition value.h:114
@ nullValue
'null' value
Definition value.h:109
@ stringValue
UTF-8 string value.
Definition value.h:113
@ realValue
double value
Definition value.h:112
@ arrayValue
array value (ordered list)
Definition value.h:115
@ intValue
signed integer value
Definition value.h:110
@ objectValue
object value (collection of name/value pairs).
Definition value.h:116
@ uintValue
unsigned integer value
Definition value.h:111
OStream & operator<<(OStream &, const Value &root)
Output using the StyledStreamWriter.
int Int
Definition config.h:108
std::auto_ptr< StreamWriter > StreamWriterPtr
static void appendHex(String &result, unsigned ch)
Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision)
Return iterator that would be the new end of the range [begin,end), if we were to delete zeros in the...
Definition json_tool.h:120
std::basic_string< char, std::char_traits< char >, Allocator< char > > String
Definition config.h:132
char[uintToStringBufferSize] UIntToStringBuffer
Definition json_tool.h:74
static void uintToString(LargestUInt value, char *&current)
Converts an unsigned integer to string.
Definition json_tool.h:81
UInt64 LargestUInt
Definition config.h:124
std::ostream OStream
Definition config.h:140
PrecisionType
Type of precision for formatting of real values.
Definition value.h:129
@ decimalPlaces
we set max number of digits after "." in string
Definition value.h:131
@ significantDigits
we set max number of significant digits in string
Definition value.h:130