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 result += *c;
346 } break;
347 }
348 }
349 result += "\"";
350 return result;
351}
352
353String valueToQuotedString(const char* value) {
354 return valueToQuotedStringN(value, strlen(value));
355}
356
357// Class Writer
358// //////////////////////////////////////////////////////////////////
359Writer::~Writer() = default;
360
361// Class FastWriter
362// //////////////////////////////////////////////////////////////////
363
365
366 = default;
367
368void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
369
370void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
371
372void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
373
375 document_.clear();
376 writeValue(root);
377 if (!omitEndingLineFeed_)
378 document_ += '\n';
379 return document_;
380}
381
382void FastWriter::writeValue(const Value& value) {
383 switch (value.type()) {
384 case nullValue:
385 if (!dropNullPlaceholders_)
386 document_ += "null";
387 break;
388 case intValue:
389 document_ += valueToString(value.asLargestInt());
390 break;
391 case uintValue:
392 document_ += valueToString(value.asLargestUInt());
393 break;
394 case realValue:
395 document_ += valueToString(value.asDouble());
396 break;
397 case stringValue: {
398 // Is NULL possible for value.string_? No.
399 char const* str;
400 char const* end;
401 bool ok = value.getString(&str, &end);
402 if (ok)
403 document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
404 break;
405 }
406 case booleanValue:
407 document_ += valueToString(value.asBool());
408 break;
409 case arrayValue: {
410 document_ += '[';
411 ArrayIndex size = value.size();
412 for (ArrayIndex index = 0; index < size; ++index) {
413 if (index > 0)
414 document_ += ',';
415 writeValue(value[index]);
416 }
417 document_ += ']';
418 } break;
419 case objectValue: {
420 Value::Members members(value.getMemberNames());
421 document_ += '{';
422 for (auto it = members.begin(); it != members.end(); ++it) {
423 const String& name = *it;
424 if (it != members.begin())
425 document_ += ',';
426 document_ += valueToQuotedStringN(name.data(), name.length());
427 document_ += yamlCompatibilityEnabled_ ? ": " : ":";
428 writeValue(value[name]);
429 }
430 document_ += '}';
431 } break;
432 }
433}
434
435// Class StyledWriter
436// //////////////////////////////////////////////////////////////////
437
439
441 document_.clear();
442 addChildValues_ = false;
443 indentString_.clear();
444 writeCommentBeforeValue(root);
445 writeValue(root);
446 writeCommentAfterValueOnSameLine(root);
447 document_ += '\n';
448 return document_;
449}
450
451void StyledWriter::writeValue(const Value& value) {
452 switch (value.type()) {
453 case nullValue:
454 pushValue("null");
455 break;
456 case intValue:
457 pushValue(valueToString(value.asLargestInt()));
458 break;
459 case uintValue:
460 pushValue(valueToString(value.asLargestUInt()));
461 break;
462 case realValue:
463 pushValue(valueToString(value.asDouble()));
464 break;
465 case stringValue: {
466 // Is NULL possible for value.string_? No.
467 char const* str;
468 char const* end;
469 bool ok = value.getString(&str, &end);
470 if (ok)
471 pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
472 else
473 pushValue("");
474 break;
475 }
476 case booleanValue:
477 pushValue(valueToString(value.asBool()));
478 break;
479 case arrayValue:
480 writeArrayValue(value);
481 break;
482 case objectValue: {
483 Value::Members members(value.getMemberNames());
484 if (members.empty())
485 pushValue("{}");
486 else {
487 writeWithIndent("{");
488 indent();
489 auto it = members.begin();
490 for (;;) {
491 const String& name = *it;
492 const Value& childValue = value[name];
493 writeCommentBeforeValue(childValue);
494 writeWithIndent(valueToQuotedString(name.c_str()));
495 document_ += " : ";
496 writeValue(childValue);
497 if (++it == members.end()) {
498 writeCommentAfterValueOnSameLine(childValue);
499 break;
500 }
501 document_ += ',';
502 writeCommentAfterValueOnSameLine(childValue);
503 }
504 unindent();
505 writeWithIndent("}");
506 }
507 } break;
508 }
509}
510
511void StyledWriter::writeArrayValue(const Value& value) {
512 size_t size = value.size();
513 if (size == 0)
514 pushValue("[]");
515 else {
516 bool isArrayMultiLine = isMultilineArray(value);
517 if (isArrayMultiLine) {
518 writeWithIndent("[");
519 indent();
520 bool hasChildValue = !childValues_.empty();
521 ArrayIndex index = 0;
522 for (;;) {
523 const Value& childValue = value[index];
524 writeCommentBeforeValue(childValue);
525 if (hasChildValue)
526 writeWithIndent(childValues_[index]);
527 else {
528 writeIndent();
529 writeValue(childValue);
530 }
531 if (++index == size) {
532 writeCommentAfterValueOnSameLine(childValue);
533 break;
534 }
535 document_ += ',';
536 writeCommentAfterValueOnSameLine(childValue);
537 }
538 unindent();
539 writeWithIndent("]");
540 } else // output on a single line
541 {
542 assert(childValues_.size() == size);
543 document_ += "[ ";
544 for (size_t index = 0; index < size; ++index) {
545 if (index > 0)
546 document_ += ", ";
547 document_ += childValues_[index];
548 }
549 document_ += " ]";
550 }
551 }
552}
553
554bool StyledWriter::isMultilineArray(const Value& value) {
555 ArrayIndex const size = value.size();
556 bool isMultiLine = size * 3 >= rightMargin_;
557 childValues_.clear();
558 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
559 const Value& childValue = value[index];
560 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
561 !childValue.empty());
562 }
563 if (!isMultiLine) // check if line length > max line length
564 {
565 childValues_.reserve(size);
566 addChildValues_ = true;
567 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
568 for (ArrayIndex index = 0; index < size; ++index) {
569 if (hasCommentForValue(value[index])) {
570 isMultiLine = true;
571 }
572 writeValue(value[index]);
573 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
574 }
575 addChildValues_ = false;
576 isMultiLine = isMultiLine || lineLength >= rightMargin_;
577 }
578 return isMultiLine;
579}
580
581void StyledWriter::pushValue(const String& value) {
582 if (addChildValues_)
583 childValues_.push_back(value);
584 else
585 document_ += value;
586}
587
588void StyledWriter::writeIndent() {
589 if (!document_.empty()) {
590 char last = document_[document_.length() - 1];
591 if (last == ' ') // already indented
592 return;
593 if (last != '\n') // Comments may add new-line
594 document_ += '\n';
595 }
596 document_ += indentString_;
597}
598
599void StyledWriter::writeWithIndent(const String& value) {
600 writeIndent();
601 document_ += value;
602}
603
604void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
605
606void StyledWriter::unindent() {
607 assert(indentString_.size() >= indentSize_);
608 indentString_.resize(indentString_.size() - indentSize_);
609}
610
611void StyledWriter::writeCommentBeforeValue(const Value& root) {
612 if (!root.hasComment(commentBefore))
613 return;
614
615 document_ += '\n';
616 writeIndent();
617 const String& comment = root.getComment(commentBefore);
618 String::const_iterator iter = comment.begin();
619 while (iter != comment.end()) {
620 document_ += *iter;
621 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
622 writeIndent();
623 ++iter;
624 }
625
626 // Comments are stripped of trailing newlines, so add one here
627 document_ += '\n';
628}
629
630void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
631 if (root.hasComment(commentAfterOnSameLine))
632 document_ += " " + root.getComment(commentAfterOnSameLine);
633
634 if (root.hasComment(commentAfter)) {
635 document_ += '\n';
636 document_ += root.getComment(commentAfter);
637 document_ += '\n';
638 }
639}
640
641bool StyledWriter::hasCommentForValue(const Value& value) {
642 return value.hasComment(commentBefore) ||
643 value.hasComment(commentAfterOnSameLine) ||
644 value.hasComment(commentAfter);
645}
646
647// Class StyledStreamWriter
648// //////////////////////////////////////////////////////////////////
649
651 : document_(nullptr), indentation_(std::move(indentation)),
652 addChildValues_(), indented_(false) {}
653
654void StyledStreamWriter::write(OStream& out, const Value& root) {
655 document_ = &out;
656 addChildValues_ = false;
657 indentString_.clear();
658 indented_ = true;
659 writeCommentBeforeValue(root);
660 if (!indented_)
661 writeIndent();
662 indented_ = true;
663 writeValue(root);
664 writeCommentAfterValueOnSameLine(root);
665 *document_ << "\n";
666 document_ = nullptr; // Forget the stream, for safety.
667}
668
669void StyledStreamWriter::writeValue(const Value& value) {
670 switch (value.type()) {
671 case nullValue:
672 pushValue("null");
673 break;
674 case intValue:
675 pushValue(valueToString(value.asLargestInt()));
676 break;
677 case uintValue:
678 pushValue(valueToString(value.asLargestUInt()));
679 break;
680 case realValue:
681 pushValue(valueToString(value.asDouble()));
682 break;
683 case stringValue: {
684 // Is NULL possible for value.string_? No.
685 char const* str;
686 char const* end;
687 bool ok = value.getString(&str, &end);
688 if (ok)
689 pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
690 else
691 pushValue("");
692 break;
693 }
694 case booleanValue:
695 pushValue(valueToString(value.asBool()));
696 break;
697 case arrayValue:
698 writeArrayValue(value);
699 break;
700 case objectValue: {
701 Value::Members members(value.getMemberNames());
702 if (members.empty())
703 pushValue("{}");
704 else {
705 writeWithIndent("{");
706 indent();
707 auto it = members.begin();
708 for (;;) {
709 const String& name = *it;
710 const Value& childValue = value[name];
711 writeCommentBeforeValue(childValue);
712 writeWithIndent(valueToQuotedString(name.c_str()));
713 *document_ << " : ";
714 writeValue(childValue);
715 if (++it == members.end()) {
716 writeCommentAfterValueOnSameLine(childValue);
717 break;
718 }
719 *document_ << ",";
720 writeCommentAfterValueOnSameLine(childValue);
721 }
722 unindent();
723 writeWithIndent("}");
724 }
725 } break;
726 }
727}
728
729void StyledStreamWriter::writeArrayValue(const Value& value) {
730 unsigned size = value.size();
731 if (size == 0)
732 pushValue("[]");
733 else {
734 bool isArrayMultiLine = isMultilineArray(value);
735 if (isArrayMultiLine) {
736 writeWithIndent("[");
737 indent();
738 bool hasChildValue = !childValues_.empty();
739 unsigned index = 0;
740 for (;;) {
741 const Value& childValue = value[index];
742 writeCommentBeforeValue(childValue);
743 if (hasChildValue)
744 writeWithIndent(childValues_[index]);
745 else {
746 if (!indented_)
747 writeIndent();
748 indented_ = true;
749 writeValue(childValue);
750 indented_ = false;
751 }
752 if (++index == size) {
753 writeCommentAfterValueOnSameLine(childValue);
754 break;
755 }
756 *document_ << ",";
757 writeCommentAfterValueOnSameLine(childValue);
758 }
759 unindent();
760 writeWithIndent("]");
761 } else // output on a single line
762 {
763 assert(childValues_.size() == size);
764 *document_ << "[ ";
765 for (unsigned index = 0; index < size; ++index) {
766 if (index > 0)
767 *document_ << ", ";
768 *document_ << childValues_[index];
769 }
770 *document_ << " ]";
771 }
772 }
773}
774
775bool StyledStreamWriter::isMultilineArray(const Value& value) {
776 ArrayIndex const size = value.size();
777 bool isMultiLine = size * 3 >= rightMargin_;
778 childValues_.clear();
779 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
780 const Value& childValue = value[index];
781 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
782 !childValue.empty());
783 }
784 if (!isMultiLine) // check if line length > max line length
785 {
786 childValues_.reserve(size);
787 addChildValues_ = true;
788 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
789 for (ArrayIndex index = 0; index < size; ++index) {
790 if (hasCommentForValue(value[index])) {
791 isMultiLine = true;
792 }
793 writeValue(value[index]);
794 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
795 }
796 addChildValues_ = false;
797 isMultiLine = isMultiLine || lineLength >= rightMargin_;
798 }
799 return isMultiLine;
800}
801
802void StyledStreamWriter::pushValue(const String& value) {
803 if (addChildValues_)
804 childValues_.push_back(value);
805 else
806 *document_ << value;
807}
808
809void StyledStreamWriter::writeIndent() {
810 // blep intended this to look at the so-far-written string
811 // to determine whether we are already indented, but
812 // with a stream we cannot do that. So we rely on some saved state.
813 // The caller checks indented_.
814 *document_ << '\n' << indentString_;
815}
816
817void StyledStreamWriter::writeWithIndent(const String& value) {
818 if (!indented_)
819 writeIndent();
820 *document_ << value;
821 indented_ = false;
822}
823
824void StyledStreamWriter::indent() { indentString_ += indentation_; }
825
826void StyledStreamWriter::unindent() {
827 assert(indentString_.size() >= indentation_.size());
828 indentString_.resize(indentString_.size() - indentation_.size());
829}
830
831void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
832 if (!root.hasComment(commentBefore))
833 return;
834
835 if (!indented_)
836 writeIndent();
837 const String& comment = root.getComment(commentBefore);
838 String::const_iterator iter = comment.begin();
839 while (iter != comment.end()) {
840 *document_ << *iter;
841 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
842 // writeIndent(); // would include newline
843 *document_ << indentString_;
844 ++iter;
845 }
846 indented_ = false;
847}
848
849void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
850 if (root.hasComment(commentAfterOnSameLine))
851 *document_ << ' ' << root.getComment(commentAfterOnSameLine);
852
853 if (root.hasComment(commentAfter)) {
854 writeIndent();
855 *document_ << root.getComment(commentAfter);
856 }
857 indented_ = false;
858}
859
860bool StyledStreamWriter::hasCommentForValue(const Value& value) {
861 return value.hasComment(commentBefore) ||
862 value.hasComment(commentAfterOnSameLine) ||
863 value.hasComment(commentAfter);
864}
865
867// BuiltStyledStreamWriter
868
870struct CommentStyle {
872 enum Enum {
873 None,
874 Most,
875 All
876 };
877};
878
879struct BuiltStyledStreamWriter : public StreamWriter {
880 BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
881 String colonSymbol, String nullSymbol,
882 String endingLineFeedSymbol, bool useSpecialFloats,
883 bool emitUTF8, unsigned int precision,
884 PrecisionType precisionType);
885 int write(Value const& root, OStream* sout) override;
886
887private:
888 void writeValue(Value const& value);
889 void writeArrayValue(Value const& value);
890 bool isMultilineArray(Value const& value);
891 void pushValue(String const& value);
892 void writeIndent();
893 void writeWithIndent(String const& value);
894 void indent();
895 void unindent();
896 void writeCommentBeforeValue(Value const& root);
897 void writeCommentAfterValueOnSameLine(Value const& root);
898 static bool hasCommentForValue(const Value& value);
899
900 using ChildValues = std::vector<String>;
901
902 ChildValues childValues_;
903 String indentString_;
904 unsigned int rightMargin_;
905 String indentation_;
906 CommentStyle::Enum cs_;
907 String colonSymbol_;
908 String nullSymbol_;
909 String endingLineFeedSymbol_;
910 bool addChildValues_ : 1;
911 bool indented_ : 1;
912 bool useSpecialFloats_ : 1;
913 bool emitUTF8_ : 1;
914 unsigned int precision_;
915 PrecisionType precisionType_;
916};
917BuiltStyledStreamWriter::BuiltStyledStreamWriter(
918 String indentation, CommentStyle::Enum cs, String colonSymbol,
919 String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
920 bool emitUTF8, unsigned int precision, PrecisionType precisionType)
921 : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
922 colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
923 endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
924 addChildValues_(false), indented_(false),
925 useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
926 precision_(precision), precisionType_(precisionType) {}
927int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
928 sout_ = sout;
929 addChildValues_ = false;
930 indented_ = true;
931 indentString_.clear();
932 writeCommentBeforeValue(root);
933 if (!indented_)
934 writeIndent();
935 indented_ = true;
936 writeValue(root);
937 writeCommentAfterValueOnSameLine(root);
938 *sout_ << endingLineFeedSymbol_;
939 sout_ = nullptr;
940 return 0;
941}
942void BuiltStyledStreamWriter::writeValue(Value const& value) {
943 switch (value.type()) {
944 case nullValue:
945 pushValue(nullSymbol_);
946 break;
947 case intValue:
948 pushValue(valueToString(value.asLargestInt()));
949 break;
950 case uintValue:
951 pushValue(valueToString(value.asLargestUInt()));
952 break;
953 case realValue:
954 pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
955 precisionType_));
956 break;
957 case stringValue: {
958 // Is NULL is possible for value.string_? No.
959 char const* str;
960 char const* end;
961 bool ok = value.getString(&str, &end);
962 if (ok)
963 pushValue(
964 valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
965 else
966 pushValue("");
967 break;
968 }
969 case booleanValue:
970 pushValue(valueToString(value.asBool()));
971 break;
972 case arrayValue:
973 writeArrayValue(value);
974 break;
975 case objectValue: {
976 Value::Members members(value.getMemberNames());
977 if (members.empty())
978 pushValue("{}");
979 else {
980 writeWithIndent("{");
981 indent();
982 auto it = members.begin();
983 for (;;) {
984 String const& name = *it;
985 Value const& childValue = value[name];
986 writeCommentBeforeValue(childValue);
987 writeWithIndent(
988 valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
989 *sout_ << colonSymbol_;
990 writeValue(childValue);
991 if (++it == members.end()) {
992 writeCommentAfterValueOnSameLine(childValue);
993 break;
994 }
995 *sout_ << ",";
996 writeCommentAfterValueOnSameLine(childValue);
997 }
998 unindent();
999 writeWithIndent("}");
1000 }
1001 } break;
1002 }
1003}
1004
1005void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
1006 unsigned size = value.size();
1007 if (size == 0)
1008 pushValue("[]");
1009 else {
1010 bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
1011 if (isMultiLine) {
1012 writeWithIndent("[");
1013 indent();
1014 bool hasChildValue = !childValues_.empty();
1015 unsigned index = 0;
1016 for (;;) {
1017 Value const& childValue = value[index];
1018 writeCommentBeforeValue(childValue);
1019 if (hasChildValue)
1020 writeWithIndent(childValues_[index]);
1021 else {
1022 if (!indented_)
1023 writeIndent();
1024 indented_ = true;
1025 writeValue(childValue);
1026 indented_ = false;
1027 }
1028 if (++index == size) {
1029 writeCommentAfterValueOnSameLine(childValue);
1030 break;
1031 }
1032 *sout_ << ",";
1033 writeCommentAfterValueOnSameLine(childValue);
1034 }
1035 unindent();
1036 writeWithIndent("]");
1037 } else // output on a single line
1038 {
1039 assert(childValues_.size() == size);
1040 *sout_ << "[";
1041 if (!indentation_.empty())
1042 *sout_ << " ";
1043 for (unsigned index = 0; index < size; ++index) {
1044 if (index > 0)
1045 *sout_ << ((!indentation_.empty()) ? ", " : ",");
1046 *sout_ << childValues_[index];
1047 }
1048 if (!indentation_.empty())
1049 *sout_ << " ";
1050 *sout_ << "]";
1051 }
1052 }
1053}
1054
1055bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
1056 ArrayIndex const size = value.size();
1057 bool isMultiLine = size * 3 >= rightMargin_;
1058 childValues_.clear();
1059 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1060 Value const& childValue = value[index];
1061 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1062 !childValue.empty());
1063 }
1064 if (!isMultiLine) // check if line length > max line length
1065 {
1066 childValues_.reserve(size);
1067 addChildValues_ = true;
1068 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1069 for (ArrayIndex index = 0; index < size; ++index) {
1070 if (hasCommentForValue(value[index])) {
1071 isMultiLine = true;
1072 }
1073 writeValue(value[index]);
1074 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1075 }
1076 addChildValues_ = false;
1077 isMultiLine = isMultiLine || lineLength >= rightMargin_;
1078 }
1079 return isMultiLine;
1080}
1081
1082void BuiltStyledStreamWriter::pushValue(String const& value) {
1083 if (addChildValues_)
1084 childValues_.push_back(value);
1085 else
1086 *sout_ << value;
1087}
1088
1089void BuiltStyledStreamWriter::writeIndent() {
1090 // blep intended this to look at the so-far-written string
1091 // to determine whether we are already indented, but
1092 // with a stream we cannot do that. So we rely on some saved state.
1093 // The caller checks indented_.
1094
1095 if (!indentation_.empty()) {
1096 // In this case, drop newlines too.
1097 *sout_ << '\n' << indentString_;
1098 }
1099}
1100
1101void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1102 if (!indented_)
1103 writeIndent();
1104 *sout_ << value;
1105 indented_ = false;
1106}
1107
1108void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1109
1110void BuiltStyledStreamWriter::unindent() {
1111 assert(indentString_.size() >= indentation_.size());
1112 indentString_.resize(indentString_.size() - indentation_.size());
1113}
1114
1115void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1116 if (cs_ == CommentStyle::None)
1117 return;
1118 if (!root.hasComment(commentBefore))
1119 return;
1120
1121 if (!indented_)
1122 writeIndent();
1123 const String& comment = root.getComment(commentBefore);
1124 String::const_iterator iter = comment.begin();
1125 while (iter != comment.end()) {
1126 *sout_ << *iter;
1127 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
1128 // writeIndent(); // would write extra newline
1129 *sout_ << indentString_;
1130 ++iter;
1131 }
1132 indented_ = false;
1133}
1134
1135void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
1136 Value const& root) {
1137 if (cs_ == CommentStyle::None)
1138 return;
1139 if (root.hasComment(commentAfterOnSameLine))
1140 *sout_ << " " + root.getComment(commentAfterOnSameLine);
1141
1142 if (root.hasComment(commentAfter)) {
1143 writeIndent();
1144 *sout_ << root.getComment(commentAfter);
1145 }
1146}
1147
1148// static
1149bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1150 return value.hasComment(commentBefore) ||
1151 value.hasComment(commentAfterOnSameLine) ||
1152 value.hasComment(commentAfter);
1153}
1154
1156// StreamWriter
1157
1158StreamWriter::StreamWriter() : sout_(nullptr) {}
1159StreamWriter::~StreamWriter() = default;
1164 const String indentation = settings_["indentation"].asString();
1165 const String cs_str = settings_["commentStyle"].asString();
1166 const String pt_str = settings_["precisionType"].asString();
1167 const bool eyc = settings_["enableYAMLCompatibility"].asBool();
1168 const bool dnp = settings_["dropNullPlaceholders"].asBool();
1169 const bool usf = settings_["useSpecialFloats"].asBool();
1170 const bool emitUTF8 = settings_["emitUTF8"].asBool();
1171 unsigned int pre = settings_["precision"].asUInt();
1172 CommentStyle::Enum cs = CommentStyle::All;
1173 if (cs_str == "All") {
1174 cs = CommentStyle::All;
1175 } else if (cs_str == "None") {
1176 cs = CommentStyle::None;
1177 } else {
1178 throwRuntimeError("commentStyle must be 'All' or 'None'");
1179 }
1180 PrecisionType precisionType(significantDigits);
1181 if (pt_str == "significant") {
1182 precisionType = PrecisionType::significantDigits;
1183 } else if (pt_str == "decimal") {
1184 precisionType = PrecisionType::decimalPlaces;
1185 } else {
1186 throwRuntimeError("precisionType must be 'significant' or 'decimal'");
1187 }
1188 String colonSymbol = " : ";
1189 if (eyc) {
1190 colonSymbol = ": ";
1191 } else if (indentation.empty()) {
1192 colonSymbol = ":";
1193 }
1194 String nullSymbol = "null";
1195 if (dnp) {
1196 nullSymbol.clear();
1197 }
1198 if (pre > 17)
1199 pre = 17;
1200 String endingLineFeedSymbol;
1201 return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1202 endingLineFeedSymbol, usf, emitUTF8, pre,
1203 precisionType);
1204}
1205
1207 static const auto& valid_keys = *new std::set<String>{
1208 "indentation",
1209 "commentStyle",
1210 "enableYAMLCompatibility",
1211 "dropNullPlaceholders",
1212 "useSpecialFloats",
1213 "emitUTF8",
1214 "precision",
1215 "precisionType",
1216 };
1217 for (auto si = settings_.begin(); si != settings_.end(); ++si) {
1218 auto key = si.name();
1219 if (valid_keys.count(key))
1220 continue;
1221 if (invalid)
1222 (*invalid)[key] = *si;
1223 else
1224 return false;
1225 }
1226 return invalid ? invalid->empty() : true;
1227}
1228
1230 return settings_[key];
1231}
1232// static
1235 (*settings)["commentStyle"] = "All";
1236 (*settings)["indentation"] = "\t";
1237 (*settings)["enableYAMLCompatibility"] = false;
1238 (*settings)["dropNullPlaceholders"] = false;
1239 (*settings)["useSpecialFloats"] = false;
1240 (*settings)["emitUTF8"] = false;
1241 (*settings)["precision"] = 17;
1242 (*settings)["precisionType"] = "significant";
1244}
1245
1246String writeString(StreamWriter::Factory const& factory, Value const& root) {
1247 OStringStream sout;
1248 StreamWriterPtr const writer(factory.newStreamWriter());
1249 writer->write(root, &sout);
1250 return sout.str();
1251}
1252
1253OStream& operator<<(OStream& sout, Value const& root) {
1254 StreamWriterBuilder builder;
1255 StreamWriterPtr const writer(builder.newStreamWriter());
1256 writer->write(root, &sout);
1257 return sout;
1258}
1259
1260} // 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