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