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
84 namespace Json {
85
86 typedef StreamWriter* StreamWriterPtr;
87
valueToString(LargestInt value)88 String valueToString(LargestInt value) {
89 UIntToStringBuffer buffer;
90 char* current = buffer + sizeof(buffer);
91 if (value == Value::minLargestInt) {
92 uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
93 *--current = '-';
94 } else if (value < 0) {
95 uintToString(LargestUInt(-value), current);
96 *--current = '-';
97 } else {
98 uintToString(LargestUInt(value), current);
99 }
100 assert(current >= buffer);
101 return current;
102 }
103
valueToString(LargestUInt value)104 String valueToString(LargestUInt value) {
105 UIntToStringBuffer buffer;
106 char* current = buffer + sizeof(buffer);
107 uintToString(value, current);
108 assert(current >= buffer);
109 return current;
110 }
111
112 #if defined(JSON_HAS_INT64)
113
valueToString(Int value)114 String valueToString(Int value) { return valueToString(LargestInt(value)); }
115
valueToString(UInt value)116 String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
117
118 #endif // # if defined(JSON_HAS_INT64)
119
120 namespace {
valueToString(double value,bool useSpecialFloats,unsigned int precision,PrecisionType precisionType)121 String valueToString(double value, bool useSpecialFloats,
122 unsigned int precision, PrecisionType precisionType) {
123 // Print into the buffer. We need not request the alternative representation
124 // that always has a decimal point because JSON doesn't distinguish the
125 // concepts of reals and integers.
126 if (!isfinite(value)) {
127 static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
128 {"null", "-1e+9999", "1e+9999"}};
129 return reps[useSpecialFloats ? 0 : 1]
130 [isnan(value) ? 0 : (value < 0) ? 1 : 2];
131 }
132
133 String buffer(size_t(36), '\0');
134 while (true) {
135 int len =
136 jsoncpp_snprintf(&*buffer.begin(), buffer.size(),
137 (precisionType == significantDigits) ? "%.*g" : "%.*f",
138 precision, value);
139 assert(len >= 0);
140 size_t wouldPrint = static_cast<size_t>(len);
141 if (wouldPrint >= buffer.size()) {
142 buffer.resize(wouldPrint + 1);
143 continue;
144 }
145 buffer.resize(wouldPrint);
146 break;
147 }
148
149 buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
150
151 // strip the zero padding from the right
152 if (precisionType == decimalPlaces) {
153 buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
154 }
155
156 // try to ensure we preserve the fact that this was given to us as a double on
157 // input
158 if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
159 buffer += ".0";
160 }
161 return buffer;
162 }
163 } // namespace
164
valueToString(double value,unsigned int precision,PrecisionType precisionType)165 String valueToString(double value, unsigned int precision,
166 PrecisionType precisionType) {
167 return valueToString(value, false, precision, precisionType);
168 }
169
valueToString(bool value)170 String valueToString(bool value) { return value ? "true" : "false"; }
171
isAnyCharRequiredQuoting(char const * s,size_t n)172 static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
173 assert(s || !n);
174
175 char const* const end = s + n;
176 for (char const* cur = s; cur < end; ++cur) {
177 if (*cur == '\\' || *cur == '\"' ||
178 static_cast<unsigned char>(*cur) < ' ' ||
179 static_cast<unsigned char>(*cur) >= 0x80)
180 return true;
181 }
182 return false;
183 }
184
utf8ToCodepoint(const char * & s,const char * e)185 static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
186 const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
187
188 unsigned int firstByte = static_cast<unsigned char>(*s);
189
190 if (firstByte < 0x80)
191 return firstByte;
192
193 if (firstByte < 0xE0) {
194 if (e - s < 2)
195 return REPLACEMENT_CHARACTER;
196
197 unsigned int calculated =
198 ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
199 s += 1;
200 // oversized encoded characters are invalid
201 return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
202 }
203
204 if (firstByte < 0xF0) {
205 if (e - s < 3)
206 return REPLACEMENT_CHARACTER;
207
208 unsigned int calculated = ((firstByte & 0x0F) << 12) |
209 ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
210 (static_cast<unsigned int>(s[2]) & 0x3F);
211 s += 2;
212 // surrogates aren't valid codepoints itself
213 // shouldn't be UTF-8 encoded
214 if (calculated >= 0xD800 && calculated <= 0xDFFF)
215 return REPLACEMENT_CHARACTER;
216 // oversized encoded characters are invalid
217 return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
218 }
219
220 if (firstByte < 0xF8) {
221 if (e - s < 4)
222 return REPLACEMENT_CHARACTER;
223
224 unsigned int calculated = ((firstByte & 0x07) << 18) |
225 ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
226 ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
227 (static_cast<unsigned int>(s[3]) & 0x3F);
228 s += 3;
229 // oversized encoded characters are invalid
230 return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
231 }
232
233 return REPLACEMENT_CHARACTER;
234 }
235
236 static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
237 "101112131415161718191a1b1c1d1e1f"
238 "202122232425262728292a2b2c2d2e2f"
239 "303132333435363738393a3b3c3d3e3f"
240 "404142434445464748494a4b4c4d4e4f"
241 "505152535455565758595a5b5c5d5e5f"
242 "606162636465666768696a6b6c6d6e6f"
243 "707172737475767778797a7b7c7d7e7f"
244 "808182838485868788898a8b8c8d8e8f"
245 "909192939495969798999a9b9c9d9e9f"
246 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
247 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
248 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
249 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
250 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
251 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
252
toHex16Bit(unsigned int x)253 static String toHex16Bit(unsigned int x) {
254 const unsigned int hi = (x >> 8) & 0xff;
255 const unsigned int lo = x & 0xff;
256 String result(4, ' ');
257 result[0] = hex2[2 * hi];
258 result[1] = hex2[2 * hi + 1];
259 result[2] = hex2[2 * lo];
260 result[3] = hex2[2 * lo + 1];
261 return result;
262 }
263
valueToQuotedStringN(const char * value,unsigned length,bool emitUTF8=false)264 static String valueToQuotedStringN(const char* value, unsigned length,
265 bool emitUTF8 = false) {
266 if (value == JSONCPP_NULL)
267 return "";
268
269 if (!isAnyCharRequiredQuoting(value, length))
270 return String("\"") + value + "\"";
271 // We have to walk value and escape any special characters.
272 // Appending to String is not efficient, but this should be rare.
273 // (Note: forward slashes are *not* rare, but I am not escaping them.)
274 String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
275 String result;
276 result.reserve(maxsize); // to avoid lots of mallocs
277 result += "\"";
278 char const* end = value + length;
279 for (const char* c = value; c != end; ++c) {
280 switch (*c) {
281 case '\"':
282 result += "\\\"";
283 break;
284 case '\\':
285 result += "\\\\";
286 break;
287 case '\b':
288 result += "\\b";
289 break;
290 case '\f':
291 result += "\\f";
292 break;
293 case '\n':
294 result += "\\n";
295 break;
296 case '\r':
297 result += "\\r";
298 break;
299 case '\t':
300 result += "\\t";
301 break;
302 // case '/':
303 // Even though \/ is considered a legal escape in JSON, a bare
304 // slash is also legal, so I see no reason to escape it.
305 // (I hope I am not misunderstanding something.)
306 // blep notes: actually escaping \/ may be useful in javascript to avoid </
307 // sequence.
308 // Should add a flag to allow this compatibility mode and prevent this
309 // sequence from occurring.
310 default: {
311 if (emitUTF8) {
312 result += *c;
313 } else {
314 unsigned int codepoint = utf8ToCodepoint(c, end);
315 const unsigned int FIRST_NON_CONTROL_CODEPOINT = 0x20;
316 const unsigned int LAST_NON_CONTROL_CODEPOINT = 0x7F;
317 const unsigned int FIRST_SURROGATE_PAIR_CODEPOINT = 0x10000;
318 // don't escape non-control characters
319 // (short escape sequence are applied above)
320 if (FIRST_NON_CONTROL_CODEPOINT <= codepoint &&
321 codepoint <= LAST_NON_CONTROL_CODEPOINT) {
322 result += static_cast<char>(codepoint);
323 } else if (codepoint <
324 FIRST_SURROGATE_PAIR_CODEPOINT) { // codepoint is in Basic
325 // Multilingual Plane
326 result += "\\u";
327 result += toHex16Bit(codepoint);
328 } else { // codepoint is not in Basic Multilingual Plane
329 // convert to surrogate pair first
330 codepoint -= FIRST_SURROGATE_PAIR_CODEPOINT;
331 result += "\\u";
332 result += toHex16Bit((codepoint >> 10) + 0xD800);
333 result += "\\u";
334 result += toHex16Bit((codepoint & 0x3FF) + 0xDC00);
335 }
336 }
337 } break;
338 }
339 }
340 result += "\"";
341 return result;
342 }
343
valueToQuotedString(const char * value)344 String valueToQuotedString(const char* value) {
345 return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
346 }
347
348 // Class Writer
349 // //////////////////////////////////////////////////////////////////
~Writer()350 Writer::~Writer() {}
351
352 // Class FastWriter
353 // //////////////////////////////////////////////////////////////////
354
FastWriter()355 FastWriter::FastWriter()
356 : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false),
357 omitEndingLineFeed_(false) {}
358
enableYAMLCompatibility()359 void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
360
dropNullPlaceholders()361 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
362
omitEndingLineFeed()363 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
364
write(const Value & root)365 String FastWriter::write(const Value& root) {
366 document_.clear();
367 writeValue(root);
368 if (!omitEndingLineFeed_)
369 document_ += '\n';
370 return document_;
371 }
372
writeValue(const Value & value)373 void FastWriter::writeValue(const Value& value) {
374 switch (value.type()) {
375 case nullValue:
376 if (!dropNullPlaceholders_)
377 document_ += "null";
378 break;
379 case intValue:
380 document_ += valueToString(value.asLargestInt());
381 break;
382 case uintValue:
383 document_ += valueToString(value.asLargestUInt());
384 break;
385 case realValue:
386 document_ += valueToString(value.asDouble());
387 break;
388 case stringValue: {
389 // Is NULL possible for value.string_? No.
390 char const* str;
391 char const* end;
392 bool ok = value.getString(&str, &end);
393 if (ok)
394 document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
395 break;
396 }
397 case booleanValue:
398 document_ += valueToString(value.asBool());
399 break;
400 case arrayValue: {
401 document_ += '[';
402 ArrayIndex size = value.size();
403 for (ArrayIndex index = 0; index < size; ++index) {
404 if (index > 0)
405 document_ += ',';
406 writeValue(value[index]);
407 }
408 document_ += ']';
409 } break;
410 case objectValue: {
411 Value::Members members(value.getMemberNames());
412 document_ += '{';
413 for (Value::Members::const_iterator it = members.begin();
414 it != members.end(); ++it) {
415 const String& name = *it;
416 if (it != members.begin())
417 document_ += ',';
418 document_ += valueToQuotedStringN(name.data(),
419 static_cast<unsigned>(name.length()));
420 document_ += yamlCompatibilityEnabled_ ? ": " : ":";
421 writeValue(value[name]);
422 }
423 document_ += '}';
424 } break;
425 }
426 }
427
428 // Class StyledWriter
429 // //////////////////////////////////////////////////////////////////
430
StyledWriter()431 StyledWriter::StyledWriter()
432 : rightMargin_(74), indentSize_(3), addChildValues_() {}
433
write(const Value & root)434 String StyledWriter::write(const Value& root) {
435 document_.clear();
436 addChildValues_ = false;
437 indentString_.clear();
438 writeCommentBeforeValue(root);
439 writeValue(root);
440 writeCommentAfterValueOnSameLine(root);
441 document_ += '\n';
442 return document_;
443 }
444
writeValue(const Value & value)445 void StyledWriter::writeValue(const Value& value) {
446 switch (value.type()) {
447 case nullValue:
448 pushValue("null");
449 break;
450 case intValue:
451 pushValue(valueToString(value.asLargestInt()));
452 break;
453 case uintValue:
454 pushValue(valueToString(value.asLargestUInt()));
455 break;
456 case realValue:
457 pushValue(valueToString(value.asDouble()));
458 break;
459 case stringValue: {
460 // Is NULL possible for value.string_? No.
461 char const* str;
462 char const* end;
463 bool ok = value.getString(&str, &end);
464 if (ok)
465 pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
466 else
467 pushValue("");
468 break;
469 }
470 case booleanValue:
471 pushValue(valueToString(value.asBool()));
472 break;
473 case arrayValue:
474 writeArrayValue(value);
475 break;
476 case objectValue: {
477 Value::Members members(value.getMemberNames());
478 if (members.empty())
479 pushValue("{}");
480 else {
481 writeWithIndent("{");
482 indent();
483 Value::Members::const_iterator it = members.begin();
484 for (;;) {
485 const String& name = *it;
486 const Value& childValue = value[name];
487 writeCommentBeforeValue(childValue);
488 writeWithIndent(valueToQuotedString(name.c_str()));
489 document_ += " : ";
490 writeValue(childValue);
491 if (++it == members.end()) {
492 writeCommentAfterValueOnSameLine(childValue);
493 break;
494 }
495 document_ += ',';
496 writeCommentAfterValueOnSameLine(childValue);
497 }
498 unindent();
499 writeWithIndent("}");
500 }
501 } break;
502 }
503 }
504
writeArrayValue(const Value & value)505 void StyledWriter::writeArrayValue(const Value& value) {
506 unsigned size = value.size();
507 if (size == 0)
508 pushValue("[]");
509 else {
510 bool isArrayMultiLine = isMultilineArray(value);
511 if (isArrayMultiLine) {
512 writeWithIndent("[");
513 indent();
514 bool hasChildValue = !childValues_.empty();
515 unsigned index = 0;
516 for (;;) {
517 const Value& childValue = value[index];
518 writeCommentBeforeValue(childValue);
519 if (hasChildValue)
520 writeWithIndent(childValues_[index]);
521 else {
522 writeIndent();
523 writeValue(childValue);
524 }
525 if (++index == size) {
526 writeCommentAfterValueOnSameLine(childValue);
527 break;
528 }
529 document_ += ',';
530 writeCommentAfterValueOnSameLine(childValue);
531 }
532 unindent();
533 writeWithIndent("]");
534 } else // output on a single line
535 {
536 assert(childValues_.size() == size);
537 document_ += "[ ";
538 for (unsigned index = 0; index < size; ++index) {
539 if (index > 0)
540 document_ += ", ";
541 document_ += childValues_[index];
542 }
543 document_ += " ]";
544 }
545 }
546 }
547
isMultilineArray(const Value & value)548 bool StyledWriter::isMultilineArray(const Value& value) {
549 ArrayIndex const size = value.size();
550 bool isMultiLine = size * 3 >= rightMargin_;
551 childValues_.clear();
552 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
553 const Value& childValue = value[index];
554 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
555 !childValue.empty());
556 }
557 if (!isMultiLine) // check if line length > max line length
558 {
559 childValues_.reserve(size);
560 addChildValues_ = true;
561 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
562 for (ArrayIndex index = 0; index < size; ++index) {
563 if (hasCommentForValue(value[index])) {
564 isMultiLine = true;
565 }
566 writeValue(value[index]);
567 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
568 }
569 addChildValues_ = false;
570 isMultiLine = isMultiLine || lineLength >= rightMargin_;
571 }
572 return isMultiLine;
573 }
574
pushValue(const String & value)575 void StyledWriter::pushValue(const String& value) {
576 if (addChildValues_)
577 childValues_.push_back(value);
578 else
579 document_ += value;
580 }
581
writeIndent()582 void StyledWriter::writeIndent() {
583 if (!document_.empty()) {
584 char last = document_[document_.length() - 1];
585 if (last == ' ') // already indented
586 return;
587 if (last != '\n') // Comments may add new-line
588 document_ += '\n';
589 }
590 document_ += indentString_;
591 }
592
writeWithIndent(const String & value)593 void StyledWriter::writeWithIndent(const String& value) {
594 writeIndent();
595 document_ += value;
596 }
597
indent()598 void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
599
unindent()600 void StyledWriter::unindent() {
601 assert(indentString_.size() >= indentSize_);
602 indentString_.resize(indentString_.size() - indentSize_);
603 }
604
writeCommentBeforeValue(const Value & root)605 void StyledWriter::writeCommentBeforeValue(const Value& root) {
606 if (!root.hasComment(commentBefore))
607 return;
608
609 document_ += '\n';
610 writeIndent();
611 const String& comment = root.getComment(commentBefore);
612 String::const_iterator iter = comment.begin();
613 while (iter != comment.end()) {
614 document_ += *iter;
615 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
616 writeIndent();
617 ++iter;
618 }
619
620 // Comments are stripped of trailing newlines, so add one here
621 document_ += '\n';
622 }
623
writeCommentAfterValueOnSameLine(const Value & root)624 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
625 if (root.hasComment(commentAfterOnSameLine))
626 document_ += " " + root.getComment(commentAfterOnSameLine);
627
628 if (root.hasComment(commentAfter)) {
629 document_ += '\n';
630 document_ += root.getComment(commentAfter);
631 document_ += '\n';
632 }
633 }
634
hasCommentForValue(const Value & value)635 bool StyledWriter::hasCommentForValue(const Value& value) {
636 return value.hasComment(commentBefore) ||
637 value.hasComment(commentAfterOnSameLine) ||
638 value.hasComment(commentAfter);
639 }
640
641 // Class StyledStreamWriter
642 // //////////////////////////////////////////////////////////////////
643
StyledStreamWriter(String indentation)644 StyledStreamWriter::StyledStreamWriter(String indentation)
645 : document_(JSONCPP_NULL), rightMargin_(74),
646 indentation_(JSONCPP_MOVE(indentation)), addChildValues_(),
647 indented_(false) {}
648
write(OStream & out,const Value & root)649 void 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_ = JSONCPP_NULL; // Forget the stream, for safety.
662 }
663
writeValue(const Value & value)664 void 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 Value::Members::const_iterator 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
writeArrayValue(const Value & value)724 void 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
isMultilineArray(const Value & value)770 bool 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
pushValue(const String & value)797 void StyledStreamWriter::pushValue(const String& value) {
798 if (addChildValues_)
799 childValues_.push_back(value);
800 else
801 *document_ << value;
802 }
803
writeIndent()804 void 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
writeWithIndent(const String & value)812 void StyledStreamWriter::writeWithIndent(const String& value) {
813 if (!indented_)
814 writeIndent();
815 *document_ << value;
816 indented_ = false;
817 }
818
indent()819 void StyledStreamWriter::indent() { indentString_ += indentation_; }
820
unindent()821 void StyledStreamWriter::unindent() {
822 assert(indentString_.size() >= indentation_.size());
823 indentString_.resize(indentString_.size() - indentation_.size());
824 }
825
writeCommentBeforeValue(const Value & root)826 void 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
writeCommentAfterValueOnSameLine(const Value & root)844 void 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
hasCommentForValue(const Value & value)855 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
856 return value.hasComment(commentBefore) ||
857 value.hasComment(commentAfterOnSameLine) ||
858 value.hasComment(commentAfter);
859 }
860
861 //////////////////////////
862 // BuiltStyledStreamWriter
863
864 /// Scoped enums are not available until C++11.
865 struct CommentStyle {
866 /// Decide whether to write comments.
867 enum Enum {
868 None, ///< Drop all comments.
869 Most, ///< Recover odd behavior of previous versions (not implemented yet).
870 All ///< Keep all comments.
871 };
872 };
873
874 struct 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) JSONCPP_OVERRIDE;
881
882 private:
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 typedef std::vector<String> ChildValues;
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 };
BuiltStyledStreamWriter(String indentation,CommentStyle::Enum cs,String colonSymbol,String nullSymbol,String endingLineFeedSymbol,bool useSpecialFloats,bool emitUTF8,unsigned int precision,PrecisionType precisionType)912 BuiltStyledStreamWriter::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_(JSONCPP_MOVE(indentation)), cs_(cs),
917 colonSymbol_(JSONCPP_MOVE(colonSymbol)),
918 nullSymbol_(JSONCPP_MOVE(nullSymbol)),
919 endingLineFeedSymbol_(JSONCPP_MOVE(endingLineFeedSymbol)),
920 addChildValues_(false), indented_(false),
921 useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
922 precision_(precision), precisionType_(precisionType) {}
write(Value const & root,OStream * sout)923 int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
924 sout_ = sout;
925 addChildValues_ = false;
926 indented_ = true;
927 indentString_.clear();
928 writeCommentBeforeValue(root);
929 if (!indented_)
930 writeIndent();
931 indented_ = true;
932 writeValue(root);
933 writeCommentAfterValueOnSameLine(root);
934 *sout_ << endingLineFeedSymbol_;
935 sout_ = JSONCPP_NULL;
936 return 0;
937 }
writeValue(Value const & value)938 void BuiltStyledStreamWriter::writeValue(Value const& value) {
939 switch (value.type()) {
940 case nullValue:
941 pushValue(nullSymbol_);
942 break;
943 case intValue:
944 pushValue(valueToString(value.asLargestInt()));
945 break;
946 case uintValue:
947 pushValue(valueToString(value.asLargestUInt()));
948 break;
949 case realValue:
950 pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
951 precisionType_));
952 break;
953 case stringValue: {
954 // Is NULL is possible for value.string_? No.
955 char const* str;
956 char const* end;
957 bool ok = value.getString(&str, &end);
958 if (ok)
959 pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str),
960 emitUTF8_));
961 else
962 pushValue("");
963 break;
964 }
965 case booleanValue:
966 pushValue(valueToString(value.asBool()));
967 break;
968 case arrayValue:
969 writeArrayValue(value);
970 break;
971 case objectValue: {
972 Value::Members members(value.getMemberNames());
973 if (members.empty())
974 pushValue("{}");
975 else {
976 writeWithIndent("{");
977 indent();
978 Value::Members::const_iterator it = members.begin();
979 for (;;) {
980 String const& name = *it;
981 Value const& childValue = value[name];
982 writeCommentBeforeValue(childValue);
983 writeWithIndent(valueToQuotedStringN(
984 name.data(), static_cast<unsigned>(name.length()), emitUTF8_));
985 *sout_ << colonSymbol_;
986 writeValue(childValue);
987 if (++it == members.end()) {
988 writeCommentAfterValueOnSameLine(childValue);
989 break;
990 }
991 *sout_ << ",";
992 writeCommentAfterValueOnSameLine(childValue);
993 }
994 unindent();
995 writeWithIndent("}");
996 }
997 } break;
998 }
999 }
1000
writeArrayValue(Value const & value)1001 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
1002 unsigned size = value.size();
1003 if (size == 0)
1004 pushValue("[]");
1005 else {
1006 bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
1007 if (isMultiLine) {
1008 writeWithIndent("[");
1009 indent();
1010 bool hasChildValue = !childValues_.empty();
1011 unsigned index = 0;
1012 for (;;) {
1013 Value const& childValue = value[index];
1014 writeCommentBeforeValue(childValue);
1015 if (hasChildValue)
1016 writeWithIndent(childValues_[index]);
1017 else {
1018 if (!indented_)
1019 writeIndent();
1020 indented_ = true;
1021 writeValue(childValue);
1022 indented_ = false;
1023 }
1024 if (++index == size) {
1025 writeCommentAfterValueOnSameLine(childValue);
1026 break;
1027 }
1028 *sout_ << ",";
1029 writeCommentAfterValueOnSameLine(childValue);
1030 }
1031 unindent();
1032 writeWithIndent("]");
1033 } else // output on a single line
1034 {
1035 assert(childValues_.size() == size);
1036 *sout_ << "[";
1037 if (!indentation_.empty())
1038 *sout_ << " ";
1039 for (unsigned index = 0; index < size; ++index) {
1040 if (index > 0)
1041 *sout_ << ((!indentation_.empty()) ? ", " : ",");
1042 *sout_ << childValues_[index];
1043 }
1044 if (!indentation_.empty())
1045 *sout_ << " ";
1046 *sout_ << "]";
1047 }
1048 }
1049 }
1050
isMultilineArray(Value const & value)1051 bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
1052 ArrayIndex const size = value.size();
1053 bool isMultiLine = size * 3 >= rightMargin_;
1054 childValues_.clear();
1055 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1056 Value const& childValue = value[index];
1057 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1058 !childValue.empty());
1059 }
1060 if (!isMultiLine) // check if line length > max line length
1061 {
1062 childValues_.reserve(size);
1063 addChildValues_ = true;
1064 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1065 for (ArrayIndex index = 0; index < size; ++index) {
1066 if (hasCommentForValue(value[index])) {
1067 isMultiLine = true;
1068 }
1069 writeValue(value[index]);
1070 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1071 }
1072 addChildValues_ = false;
1073 isMultiLine = isMultiLine || lineLength >= rightMargin_;
1074 }
1075 return isMultiLine;
1076 }
1077
pushValue(String const & value)1078 void BuiltStyledStreamWriter::pushValue(String const& value) {
1079 if (addChildValues_)
1080 childValues_.push_back(value);
1081 else
1082 *sout_ << value;
1083 }
1084
writeIndent()1085 void BuiltStyledStreamWriter::writeIndent() {
1086 // blep intended this to look at the so-far-written string
1087 // to determine whether we are already indented, but
1088 // with a stream we cannot do that. So we rely on some saved state.
1089 // The caller checks indented_.
1090
1091 if (!indentation_.empty()) {
1092 // In this case, drop newlines too.
1093 *sout_ << '\n' << indentString_;
1094 }
1095 }
1096
writeWithIndent(String const & value)1097 void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1098 if (!indented_)
1099 writeIndent();
1100 *sout_ << value;
1101 indented_ = false;
1102 }
1103
indent()1104 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1105
unindent()1106 void BuiltStyledStreamWriter::unindent() {
1107 assert(indentString_.size() >= indentation_.size());
1108 indentString_.resize(indentString_.size() - indentation_.size());
1109 }
1110
writeCommentBeforeValue(Value const & root)1111 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1112 if (cs_ == CommentStyle::None)
1113 return;
1114 if (!root.hasComment(commentBefore))
1115 return;
1116
1117 if (!indented_)
1118 writeIndent();
1119 const String& comment = root.getComment(commentBefore);
1120 String::const_iterator iter = comment.begin();
1121 while (iter != comment.end()) {
1122 *sout_ << *iter;
1123 if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
1124 // writeIndent(); // would write extra newline
1125 *sout_ << indentString_;
1126 ++iter;
1127 }
1128 indented_ = false;
1129 }
1130
writeCommentAfterValueOnSameLine(Value const & root)1131 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
1132 Value const& root) {
1133 if (cs_ == CommentStyle::None)
1134 return;
1135 if (root.hasComment(commentAfterOnSameLine))
1136 *sout_ << " " + root.getComment(commentAfterOnSameLine);
1137
1138 if (root.hasComment(commentAfter)) {
1139 writeIndent();
1140 *sout_ << root.getComment(commentAfter);
1141 }
1142 }
1143
1144 // static
hasCommentForValue(const Value & value)1145 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1146 return value.hasComment(commentBefore) ||
1147 value.hasComment(commentAfterOnSameLine) ||
1148 value.hasComment(commentAfter);
1149 }
1150
1151 ///////////////
1152 // StreamWriter
1153
StreamWriter()1154 StreamWriter::StreamWriter() : sout_(JSONCPP_NULL) {}
~StreamWriter()1155 StreamWriter::~StreamWriter() {}
~Factory()1156 StreamWriter::Factory::~Factory() {}
StreamWriterBuilder()1157 StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
~StreamWriterBuilder()1158 StreamWriterBuilder::~StreamWriterBuilder() {}
newStreamWriter() const1159 StreamWriter* StreamWriterBuilder::newStreamWriter() const {
1160 const String indentation = settings_["indentation"].asString();
1161 const String cs_str = settings_["commentStyle"].asString();
1162 const String pt_str = settings_["precisionType"].asString();
1163 const bool eyc = settings_["enableYAMLCompatibility"].asBool();
1164 const bool dnp = settings_["dropNullPlaceholders"].asBool();
1165 const bool usf = settings_["useSpecialFloats"].asBool();
1166 const bool emitUTF8 = settings_["emitUTF8"].asBool();
1167 unsigned int pre = settings_["precision"].asUInt();
1168 CommentStyle::Enum cs = CommentStyle::All;
1169 if (cs_str == "All") {
1170 cs = CommentStyle::All;
1171 } else if (cs_str == "None") {
1172 cs = CommentStyle::None;
1173 } else {
1174 throwRuntimeError("commentStyle must be 'All' or 'None'");
1175 }
1176 PrecisionType precisionType(significantDigits);
1177 if (pt_str == "significant") {
1178 precisionType = significantDigits;
1179 } else if (pt_str == "decimal") {
1180 precisionType = decimalPlaces;
1181 } else {
1182 throwRuntimeError("precisionType must be 'significant' or 'decimal'");
1183 }
1184 String colonSymbol = " : ";
1185 if (eyc) {
1186 colonSymbol = ": ";
1187 } else if (indentation.empty()) {
1188 colonSymbol = ":";
1189 }
1190 String nullSymbol = "null";
1191 if (dnp) {
1192 nullSymbol.clear();
1193 }
1194 if (pre > 17)
1195 pre = 17;
1196 String endingLineFeedSymbol;
1197 return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1198 endingLineFeedSymbol, usf, emitUTF8, pre,
1199 precisionType);
1200 }
getValidWriterKeys(std::set<String> * valid_keys)1201 static void getValidWriterKeys(std::set<String>* valid_keys) {
1202 valid_keys->clear();
1203 valid_keys->insert("indentation");
1204 valid_keys->insert("commentStyle");
1205 valid_keys->insert("enableYAMLCompatibility");
1206 valid_keys->insert("dropNullPlaceholders");
1207 valid_keys->insert("useSpecialFloats");
1208 valid_keys->insert("emitUTF8");
1209 valid_keys->insert("precision");
1210 valid_keys->insert("precisionType");
1211 }
validate(Json::Value * invalid) const1212 bool StreamWriterBuilder::validate(Json::Value* invalid) const {
1213 Json::Value my_invalid;
1214 if (!invalid)
1215 invalid = &my_invalid; // so we do not need to test for NULL
1216 Json::Value& inv = *invalid;
1217 std::set<String> valid_keys;
1218 getValidWriterKeys(&valid_keys);
1219 Value::Members keys = settings_.getMemberNames();
1220 size_t n = keys.size();
1221 for (size_t i = 0; i < n; ++i) {
1222 String const& key = keys[i];
1223 if (valid_keys.find(key) == valid_keys.end()) {
1224 inv[key] = settings_[key];
1225 }
1226 }
1227 return inv.empty();
1228 }
operator [](const String & key)1229 Value& StreamWriterBuilder::operator[](const String& key) {
1230 return settings_[key];
1231 }
1232 // static
setDefaults(Json::Value * settings)1233 void StreamWriterBuilder::setDefaults(Json::Value* settings) {
1234 //! [StreamWriterBuilderDefaults]
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";
1243 //! [StreamWriterBuilderDefaults]
1244 }
1245
writeString(StreamWriter::Factory const & factory,Value const & root)1246 String writeString(StreamWriter::Factory const& factory, Value const& root) {
1247 OStringStream sout;
1248 StreamWriterPtr const writer(factory.newStreamWriter());
1249 writer->write(root, &sout);
1250 delete writer;
1251 return sout.str();
1252 }
1253
operator <<(OStream & sout,Value const & root)1254 OStream& operator<<(OStream& sout, Value const& root) {
1255 StreamWriterBuilder builder;
1256 StreamWriterPtr const writer(builder.newStreamWriter());
1257 writer->write(root, &sout);
1258 delete writer;
1259 return sout;
1260 }
1261
1262 } // namespace Json
1263