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