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