1 // Copyright 2011 Baptiste Lepilleur
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 <utility>
11 #include <assert.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sstream>
15 #include <iomanip>
16 #include <math.h>
17
18 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
19 #include <float.h>
20 #define isfinite _finite
21 #define snprintf _snprintf
22 #endif
23
24 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
25 // Disable warning about strdup being deprecated.
26 #pragma warning(disable : 4996)
27 #endif
28
29 namespace Json {
30
containsControlCharacter(const char * str)31 static bool containsControlCharacter(const char* str) {
32 while (*str) {
33 if (isControlCharacter(*(str++)))
34 return true;
35 }
36 return false;
37 }
38
valueToString(LargestInt value)39 std::string valueToString(LargestInt value) {
40 UIntToStringBuffer buffer;
41 char* current = buffer + sizeof(buffer);
42 bool isNegative = value < 0;
43 if (isNegative)
44 value = -value;
45 uintToString(LargestUInt(value), current);
46 if (isNegative)
47 *--current = '-';
48 assert(current >= buffer);
49 return current;
50 }
51
valueToString(LargestUInt value)52 std::string valueToString(LargestUInt value) {
53 UIntToStringBuffer buffer;
54 char* current = buffer + sizeof(buffer);
55 uintToString(value, current);
56 assert(current >= buffer);
57 return current;
58 }
59
60 #if defined(JSON_HAS_INT64)
61
valueToString(Int value)62 std::string valueToString(Int value) {
63 return valueToString(LargestInt(value));
64 }
65
valueToString(UInt value)66 std::string valueToString(UInt value) {
67 return valueToString(LargestUInt(value));
68 }
69
70 #endif // # if defined(JSON_HAS_INT64)
71
valueToString(double value)72 std::string valueToString(double value) {
73 // Allocate a buffer that is more than large enough to store the 16 digits of
74 // precision requested below.
75 char buffer[32];
76 int len = -1;
77
78 // Print into the buffer. We need not request the alternative representation
79 // that always has a decimal point because JSON doesn't distingish the
80 // concepts of reals and integers.
81 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
82 // visual studio 2005 to
83 // avoid warning.
84 #if defined(WINCE)
85 len = _snprintf(buffer, sizeof(buffer), "%.16g", value);
86 #else
87 len = sprintf_s(buffer, sizeof(buffer), "%.16g", value);
88 #endif
89 #else
90 if (isfinite(value)) {
91 len = snprintf(buffer, sizeof(buffer), "%.16g", value);
92 } else {
93 // IEEE standard states that NaN values will not compare to themselves
94 if (value != value) {
95 len = snprintf(buffer, sizeof(buffer), "null");
96 } else if (value < 0) {
97 len = snprintf(buffer, sizeof(buffer), "-1e+9999");
98 } else {
99 len = snprintf(buffer, sizeof(buffer), "1e+9999");
100 }
101 // For those, we do not need to call fixNumLoc, but it is fast.
102 }
103 #endif
104 assert(len >= 0);
105 fixNumericLocale(buffer, buffer + len);
106 return buffer;
107 }
108
valueToString(bool value)109 std::string valueToString(bool value) { return value ? "true" : "false"; }
110
valueToQuotedString(const char * value)111 std::string valueToQuotedString(const char* value) {
112 if (value == NULL)
113 return "";
114 // Not sure how to handle unicode...
115 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
116 !containsControlCharacter(value))
117 return std::string("\"") + value + "\"";
118 // We have to walk value and escape any special characters.
119 // Appending to std::string is not efficient, but this should be rare.
120 // (Note: forward slashes are *not* rare, but I am not escaping them.)
121 std::string::size_type maxsize =
122 strlen(value) * 2 + 3; // allescaped+quotes+NULL
123 std::string result;
124 result.reserve(maxsize); // to avoid lots of mallocs
125 result += "\"";
126 for (const char* c = value; *c != 0; ++c) {
127 switch (*c) {
128 case '\"':
129 result += "\\\"";
130 break;
131 case '\\':
132 result += "\\\\";
133 break;
134 case '\b':
135 result += "\\b";
136 break;
137 case '\f':
138 result += "\\f";
139 break;
140 case '\n':
141 result += "\\n";
142 break;
143 case '\r':
144 result += "\\r";
145 break;
146 case '\t':
147 result += "\\t";
148 break;
149 // case '/':
150 // Even though \/ is considered a legal escape in JSON, a bare
151 // slash is also legal, so I see no reason to escape it.
152 // (I hope I am not misunderstanding something.
153 // blep notes: actually escaping \/ may be useful in javascript to avoid </
154 // sequence.
155 // Should add a flag to allow this compatibility mode and prevent this
156 // sequence from occurring.
157 default:
158 if (isControlCharacter(*c)) {
159 std::ostringstream oss;
160 oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
161 << std::setw(4) << static_cast<int>(*c);
162 result += oss.str();
163 } else {
164 result += *c;
165 }
166 break;
167 }
168 }
169 result += "\"";
170 return result;
171 }
172
173 // Class Writer
174 // //////////////////////////////////////////////////////////////////
~Writer()175 Writer::~Writer() {}
176
177 // Class FastWriter
178 // //////////////////////////////////////////////////////////////////
179
FastWriter()180 FastWriter::FastWriter()
181 : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
182 omitEndingLineFeed_(false) {}
183
enableYAMLCompatibility()184 void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
185
dropNullPlaceholders()186 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
187
omitEndingLineFeed()188 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
189
write(const Value & root)190 std::string FastWriter::write(const Value& root) {
191 document_ = "";
192 writeValue(root);
193 if (!omitEndingLineFeed_)
194 document_ += "\n";
195 return document_;
196 }
197
writeValue(const Value & value)198 void FastWriter::writeValue(const Value& value) {
199 switch (value.type()) {
200 case nullValue:
201 if (!dropNullPlaceholders_)
202 document_ += "null";
203 break;
204 case intValue:
205 document_ += valueToString(value.asLargestInt());
206 break;
207 case uintValue:
208 document_ += valueToString(value.asLargestUInt());
209 break;
210 case realValue:
211 document_ += valueToString(value.asDouble());
212 break;
213 case stringValue:
214 document_ += valueToQuotedString(value.asCString());
215 break;
216 case booleanValue:
217 document_ += valueToString(value.asBool());
218 break;
219 case arrayValue: {
220 document_ += '[';
221 int size = value.size();
222 for (int index = 0; index < size; ++index) {
223 if (index > 0)
224 document_ += ',';
225 writeValue(value[index]);
226 }
227 document_ += ']';
228 } break;
229 case objectValue: {
230 Value::Members members(value.getMemberNames());
231 document_ += '{';
232 for (Value::Members::iterator it = members.begin(); it != members.end();
233 ++it) {
234 const std::string& name = *it;
235 if (it != members.begin())
236 document_ += ',';
237 document_ += valueToQuotedString(name.c_str());
238 document_ += yamlCompatiblityEnabled_ ? ": " : ":";
239 writeValue(value[name]);
240 }
241 document_ += '}';
242 } break;
243 }
244 }
245
246 // Class StyledWriter
247 // //////////////////////////////////////////////////////////////////
248
StyledWriter()249 StyledWriter::StyledWriter()
250 : rightMargin_(74), indentSize_(3), addChildValues_() {}
251
write(const Value & root)252 std::string StyledWriter::write(const Value& root) {
253 document_ = "";
254 addChildValues_ = false;
255 indentString_ = "";
256 writeCommentBeforeValue(root);
257 writeValue(root);
258 writeCommentAfterValueOnSameLine(root);
259 document_ += "\n";
260 return document_;
261 }
262
writeValue(const Value & value)263 void StyledWriter::writeValue(const Value& value) {
264 switch (value.type()) {
265 case nullValue:
266 pushValue("null");
267 break;
268 case intValue:
269 pushValue(valueToString(value.asLargestInt()));
270 break;
271 case uintValue:
272 pushValue(valueToString(value.asLargestUInt()));
273 break;
274 case realValue:
275 pushValue(valueToString(value.asDouble()));
276 break;
277 case stringValue:
278 pushValue(valueToQuotedString(value.asCString()));
279 break;
280 case booleanValue:
281 pushValue(valueToString(value.asBool()));
282 break;
283 case arrayValue:
284 writeArrayValue(value);
285 break;
286 case objectValue: {
287 Value::Members members(value.getMemberNames());
288 if (members.empty())
289 pushValue("{}");
290 else {
291 writeWithIndent("{");
292 indent();
293 Value::Members::iterator it = members.begin();
294 for (;;) {
295 const std::string& name = *it;
296 const Value& childValue = value[name];
297 writeCommentBeforeValue(childValue);
298 writeWithIndent(valueToQuotedString(name.c_str()));
299 document_ += " : ";
300 writeValue(childValue);
301 if (++it == members.end()) {
302 writeCommentAfterValueOnSameLine(childValue);
303 break;
304 }
305 document_ += ',';
306 writeCommentAfterValueOnSameLine(childValue);
307 }
308 unindent();
309 writeWithIndent("}");
310 }
311 } break;
312 }
313 }
314
writeArrayValue(const Value & value)315 void StyledWriter::writeArrayValue(const Value& value) {
316 unsigned size = value.size();
317 if (size == 0)
318 pushValue("[]");
319 else {
320 bool isArrayMultiLine = isMultineArray(value);
321 if (isArrayMultiLine) {
322 writeWithIndent("[");
323 indent();
324 bool hasChildValue = !childValues_.empty();
325 unsigned index = 0;
326 for (;;) {
327 const Value& childValue = value[index];
328 writeCommentBeforeValue(childValue);
329 if (hasChildValue)
330 writeWithIndent(childValues_[index]);
331 else {
332 writeIndent();
333 writeValue(childValue);
334 }
335 if (++index == size) {
336 writeCommentAfterValueOnSameLine(childValue);
337 break;
338 }
339 document_ += ',';
340 writeCommentAfterValueOnSameLine(childValue);
341 }
342 unindent();
343 writeWithIndent("]");
344 } else // output on a single line
345 {
346 assert(childValues_.size() == size);
347 document_ += "[ ";
348 for (unsigned index = 0; index < size; ++index) {
349 if (index > 0)
350 document_ += ", ";
351 document_ += childValues_[index];
352 }
353 document_ += " ]";
354 }
355 }
356 }
357
isMultineArray(const Value & value)358 bool StyledWriter::isMultineArray(const Value& value) {
359 int size = value.size();
360 bool isMultiLine = size * 3 >= rightMargin_;
361 childValues_.clear();
362 for (int index = 0; index < size && !isMultiLine; ++index) {
363 const Value& childValue = value[index];
364 isMultiLine =
365 isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
366 childValue.size() > 0);
367 }
368 if (!isMultiLine) // check if line length > max line length
369 {
370 childValues_.reserve(size);
371 addChildValues_ = true;
372 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
373 for (int index = 0; index < size; ++index) {
374 writeValue(value[index]);
375 lineLength += int(childValues_[index].length());
376 }
377 addChildValues_ = false;
378 isMultiLine = isMultiLine || lineLength >= rightMargin_;
379 }
380 return isMultiLine;
381 }
382
pushValue(const std::string & value)383 void StyledWriter::pushValue(const std::string& value) {
384 if (addChildValues_)
385 childValues_.push_back(value);
386 else
387 document_ += value;
388 }
389
writeIndent()390 void StyledWriter::writeIndent() {
391 if (!document_.empty()) {
392 char last = document_[document_.length() - 1];
393 if (last == ' ') // already indented
394 return;
395 if (last != '\n') // Comments may add new-line
396 document_ += '\n';
397 }
398 document_ += indentString_;
399 }
400
writeWithIndent(const std::string & value)401 void StyledWriter::writeWithIndent(const std::string& value) {
402 writeIndent();
403 document_ += value;
404 }
405
indent()406 void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
407
unindent()408 void StyledWriter::unindent() {
409 assert(int(indentString_.size()) >= indentSize_);
410 indentString_.resize(indentString_.size() - indentSize_);
411 }
412
writeCommentBeforeValue(const Value & root)413 void StyledWriter::writeCommentBeforeValue(const Value& root) {
414 if (!root.hasComment(commentBefore))
415 return;
416
417 document_ += "\n";
418 writeIndent();
419 std::string normalizedComment = normalizeEOL(root.getComment(commentBefore));
420 std::string::const_iterator iter = normalizedComment.begin();
421 while (iter != normalizedComment.end()) {
422 document_ += *iter;
423 if (*iter == '\n' && *(iter + 1) == '/')
424 writeIndent();
425 ++iter;
426 }
427
428 // Comments are stripped of newlines, so add one here
429 document_ += "\n";
430 }
431
writeCommentAfterValueOnSameLine(const Value & root)432 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
433 if (root.hasComment(commentAfterOnSameLine))
434 document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
435
436 if (root.hasComment(commentAfter)) {
437 document_ += "\n";
438 document_ += normalizeEOL(root.getComment(commentAfter));
439 document_ += "\n";
440 }
441 }
442
hasCommentForValue(const Value & value)443 bool StyledWriter::hasCommentForValue(const Value& value) {
444 return value.hasComment(commentBefore) ||
445 value.hasComment(commentAfterOnSameLine) ||
446 value.hasComment(commentAfter);
447 }
448
normalizeEOL(const std::string & text)449 std::string StyledWriter::normalizeEOL(const std::string& text) {
450 std::string normalized;
451 normalized.reserve(text.length());
452 const char* begin = text.c_str();
453 const char* end = begin + text.length();
454 const char* current = begin;
455 while (current != end) {
456 char c = *current++;
457 if (c == '\r') // mac or dos EOL
458 {
459 if (*current == '\n') // convert dos EOL
460 ++current;
461 normalized += '\n';
462 } else // handle unix EOL & other char
463 normalized += c;
464 }
465 return normalized;
466 }
467
468 // Class StyledStreamWriter
469 // //////////////////////////////////////////////////////////////////
470
StyledStreamWriter(std::string indentation)471 StyledStreamWriter::StyledStreamWriter(std::string indentation)
472 : document_(NULL), rightMargin_(74), indentation_(indentation),
473 addChildValues_() {}
474
write(std::ostream & out,const Value & root)475 void StyledStreamWriter::write(std::ostream& out, const Value& root) {
476 document_ = &out;
477 addChildValues_ = false;
478 indentString_ = "";
479 writeCommentBeforeValue(root);
480 writeValue(root);
481 writeCommentAfterValueOnSameLine(root);
482 *document_ << "\n";
483 document_ = NULL; // Forget the stream, for safety.
484 }
485
writeValue(const Value & value)486 void StyledStreamWriter::writeValue(const Value& value) {
487 switch (value.type()) {
488 case nullValue:
489 pushValue("null");
490 break;
491 case intValue:
492 pushValue(valueToString(value.asLargestInt()));
493 break;
494 case uintValue:
495 pushValue(valueToString(value.asLargestUInt()));
496 break;
497 case realValue:
498 pushValue(valueToString(value.asDouble()));
499 break;
500 case stringValue:
501 pushValue(valueToQuotedString(value.asCString()));
502 break;
503 case booleanValue:
504 pushValue(valueToString(value.asBool()));
505 break;
506 case arrayValue:
507 writeArrayValue(value);
508 break;
509 case objectValue: {
510 Value::Members members(value.getMemberNames());
511 if (members.empty())
512 pushValue("{}");
513 else {
514 writeWithIndent("{");
515 indent();
516 Value::Members::iterator it = members.begin();
517 for (;;) {
518 const std::string& name = *it;
519 const Value& childValue = value[name];
520 writeCommentBeforeValue(childValue);
521 writeWithIndent(valueToQuotedString(name.c_str()));
522 *document_ << " : ";
523 writeValue(childValue);
524 if (++it == members.end()) {
525 writeCommentAfterValueOnSameLine(childValue);
526 break;
527 }
528 *document_ << ",";
529 writeCommentAfterValueOnSameLine(childValue);
530 }
531 unindent();
532 writeWithIndent("}");
533 }
534 } break;
535 }
536 }
537
writeArrayValue(const Value & value)538 void StyledStreamWriter::writeArrayValue(const Value& value) {
539 unsigned size = value.size();
540 if (size == 0)
541 pushValue("[]");
542 else {
543 bool isArrayMultiLine = isMultineArray(value);
544 if (isArrayMultiLine) {
545 writeWithIndent("[");
546 indent();
547 bool hasChildValue = !childValues_.empty();
548 unsigned index = 0;
549 for (;;) {
550 const Value& childValue = value[index];
551 writeCommentBeforeValue(childValue);
552 if (hasChildValue)
553 writeWithIndent(childValues_[index]);
554 else {
555 writeIndent();
556 writeValue(childValue);
557 }
558 if (++index == size) {
559 writeCommentAfterValueOnSameLine(childValue);
560 break;
561 }
562 *document_ << ",";
563 writeCommentAfterValueOnSameLine(childValue);
564 }
565 unindent();
566 writeWithIndent("]");
567 } else // output on a single line
568 {
569 assert(childValues_.size() == size);
570 *document_ << "[ ";
571 for (unsigned index = 0; index < size; ++index) {
572 if (index > 0)
573 *document_ << ", ";
574 *document_ << childValues_[index];
575 }
576 *document_ << " ]";
577 }
578 }
579 }
580
isMultineArray(const Value & value)581 bool StyledStreamWriter::isMultineArray(const Value& value) {
582 int size = value.size();
583 bool isMultiLine = size * 3 >= rightMargin_;
584 childValues_.clear();
585 for (int index = 0; index < size && !isMultiLine; ++index) {
586 const Value& childValue = value[index];
587 isMultiLine =
588 isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
589 childValue.size() > 0);
590 }
591 if (!isMultiLine) // check if line length > max line length
592 {
593 childValues_.reserve(size);
594 addChildValues_ = true;
595 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
596 for (int index = 0; index < size; ++index) {
597 writeValue(value[index]);
598 lineLength += int(childValues_[index].length());
599 }
600 addChildValues_ = false;
601 isMultiLine = isMultiLine || lineLength >= rightMargin_;
602 }
603 return isMultiLine;
604 }
605
pushValue(const std::string & value)606 void StyledStreamWriter::pushValue(const std::string& value) {
607 if (addChildValues_)
608 childValues_.push_back(value);
609 else
610 *document_ << value;
611 }
612
writeIndent()613 void StyledStreamWriter::writeIndent() {
614 /*
615 Some comments in this method would have been nice. ;-)
616
617 if ( !document_.empty() )
618 {
619 char last = document_[document_.length()-1];
620 if ( last == ' ' ) // already indented
621 return;
622 if ( last != '\n' ) // Comments may add new-line
623 *document_ << '\n';
624 }
625 */
626 *document_ << '\n' << indentString_;
627 }
628
writeWithIndent(const std::string & value)629 void StyledStreamWriter::writeWithIndent(const std::string& value) {
630 writeIndent();
631 *document_ << value;
632 }
633
indent()634 void StyledStreamWriter::indent() { indentString_ += indentation_; }
635
unindent()636 void StyledStreamWriter::unindent() {
637 assert(indentString_.size() >= indentation_.size());
638 indentString_.resize(indentString_.size() - indentation_.size());
639 }
640
writeCommentBeforeValue(const Value & root)641 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
642 if (!root.hasComment(commentBefore))
643 return;
644 *document_ << normalizeEOL(root.getComment(commentBefore));
645 *document_ << "\n";
646 }
647
writeCommentAfterValueOnSameLine(const Value & root)648 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
649 if (root.hasComment(commentAfterOnSameLine))
650 *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
651
652 if (root.hasComment(commentAfter)) {
653 *document_ << "\n";
654 *document_ << normalizeEOL(root.getComment(commentAfter));
655 *document_ << "\n";
656 }
657 }
658
hasCommentForValue(const Value & value)659 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
660 return value.hasComment(commentBefore) ||
661 value.hasComment(commentAfterOnSameLine) ||
662 value.hasComment(commentAfter);
663 }
664
normalizeEOL(const std::string & text)665 std::string StyledStreamWriter::normalizeEOL(const std::string& text) {
666 std::string normalized;
667 normalized.reserve(text.length());
668 const char* begin = text.c_str();
669 const char* end = begin + text.length();
670 const char* current = begin;
671 while (current != end) {
672 char c = *current++;
673 if (c == '\r') // mac or dos EOL
674 {
675 if (*current == '\n') // convert dos EOL
676 ++current;
677 normalized += '\n';
678 } else // handle unix EOL & other char
679 normalized += c;
680 }
681 return normalized;
682 }
683
operator <<(std::ostream & sout,const Value & root)684 std::ostream& operator<<(std::ostream& sout, const Value& root) {
685 Json::StyledStreamWriter writer;
686 writer.write(sout, root);
687 return sout;
688 }
689
690 } // namespace Json
691