JsonCpp project page Classes Namespace JsonCpp home page

json_writer.cpp
Go to the documentation of this file.
1 // Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5 
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include "json_tool.h"
8 #include <json/writer.h>
9 #endif // if !defined(JSON_IS_AMALGAMATION)
10 #include <cassert>
11 #include <cstring>
12 #include <iomanip>
13 #include <memory>
14 #include <set>
15 #include <sstream>
16 #include <utility>
17 
18 #if __cplusplus >= 201103L
19 #include <cmath>
20 #include <cstdio>
21 
22 #if !defined(isnan)
23 #define isnan std::isnan
24 #endif
25 
26 #if !defined(isfinite)
27 #define isfinite std::isfinite
28 #endif
29 
30 #else
31 #include <cmath>
32 #include <cstdio>
33 
34 #if defined(_MSC_VER)
35 #if !defined(isnan)
36 #include <float.h>
37 #define isnan _isnan
38 #endif
39 
40 #if !defined(isfinite)
41 #include <float.h>
42 #define isfinite _finite
43 #endif
44 
45 #if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
46 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
47 #endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
48 
49 #endif //_MSC_VER
50 
51 #if defined(__sun) && defined(__SVR4) // Solaris
52 #if !defined(isfinite)
53 #include <ieeefp.h>
54 #define isfinite finite
55 #endif
56 #endif
57 
58 #if defined(__hpux)
59 #if !defined(isfinite)
60 #if defined(__ia64) && !defined(finite)
61 #define isfinite(x) \
62  ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
63 #endif
64 #endif
65 #endif
66 
67 #if !defined(isnan)
68 // IEEE standard states that NaN values will not compare to themselves
69 #define isnan(x) (x != x)
70 #endif
71 
72 #if !defined(__APPLE__)
73 #if !defined(isfinite)
74 #define isfinite finite
75 #endif
76 #endif
77 #endif
78 
79 #if defined(_MSC_VER)
80 // Disable warning about strdup being deprecated.
81 #pragma warning(disable : 4996)
82 #endif
83 
84 namespace Json {
85 
86 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
87 using StreamWriterPtr = std::unique_ptr<StreamWriter>;
88 #else
89 typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
90 #endif
91 
93  UIntToStringBuffer buffer;
94  char* current = buffer + sizeof(buffer);
95  if (value == Value::minLargestInt) {
97  *--current = '-';
98  } else if (value < 0) {
99  uintToString(LargestUInt(-value), current);
100  *--current = '-';
101  } else {
102  uintToString(LargestUInt(value), current);
103  }
104  assert(current >= buffer);
105  return current;
106 }
107 
109  UIntToStringBuffer buffer;
110  char* current = buffer + sizeof(buffer);
111  uintToString(value, current);
112  assert(current >= buffer);
113  return current;
114 }
115 
116 #if defined(JSON_HAS_INT64)
117 
118 String valueToString(Int value) { return valueToString(LargestInt(value)); }
119 
121 
122 #endif // # if defined(JSON_HAS_INT64)
123 
124 namespace {
125 String valueToString(double value, bool useSpecialFloats,
126  unsigned int precision, PrecisionType precisionType) {
127  // Print into the buffer. We need not request the alternative representation
128  // that always has a decimal point because JSON doesn't distinguish the
129  // concepts of reals and integers.
130  if (!isfinite(value)) {
131  static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
132  {"null", "-1e+9999", "1e+9999"}};
133  return reps[useSpecialFloats ? 0 : 1]
134  [isnan(value) ? 0 : (value < 0) ? 1 : 2];
135  }
136 
137  String buffer(size_t(36), '\0');
138  while (true) {
139  int len = jsoncpp_snprintf(
140  &*buffer.begin(), buffer.size(),
141  (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
142  precision, value);
143  assert(len >= 0);
144  auto wouldPrint = static_cast<size_t>(len);
145  if (wouldPrint >= buffer.size()) {
146  buffer.resize(wouldPrint + 1);
147  continue;
148  }
149  buffer.resize(wouldPrint);
150  break;
151  }
152 
153  buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
154 
155  // strip the zero padding from the right
156  if (precisionType == PrecisionType::decimalPlaces) {
157  buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
158  }
159 
160  // try to ensure we preserve the fact that this was given to us as a double on
161  // input
162  if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
163  buffer += ".0";
164  }
165  return buffer;
166 }
167 } // namespace
168 
169 String valueToString(double value, unsigned int precision,
170  PrecisionType precisionType) {
171  return valueToString(value, false, precision, precisionType);
172 }
173 
174 String valueToString(bool value) { return value ? "true" : "false"; }
175 
176 static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
177  assert(s || !n);
178 
179  char const* const end = s + n;
180  for (char const* cur = s; cur < end; ++cur) {
181  if (*cur == '\\' || *cur == '\"' ||
182  static_cast<unsigned char>(*cur) < ' ' ||
183  static_cast<unsigned char>(*cur) >= 0x80)
184  return true;
185  }
186  return false;
187 }
188 
189 static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
190  const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
191 
192  unsigned int firstByte = static_cast<unsigned char>(*s);
193 
194  if (firstByte < 0x80)
195  return firstByte;
196 
197  if (firstByte < 0xE0) {
198  if (e - s < 2)
199  return REPLACEMENT_CHARACTER;
200 
201  unsigned int calculated =
202  ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
203  s += 1;
204  // oversized encoded characters are invalid
205  return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
206  }
207 
208  if (firstByte < 0xF0) {
209  if (e - s < 3)
210  return REPLACEMENT_CHARACTER;
211 
212  unsigned int calculated = ((firstByte & 0x0F) << 12) |
213  ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
214  (static_cast<unsigned int>(s[2]) & 0x3F);
215  s += 2;
216  // surrogates aren't valid codepoints itself
217  // shouldn't be UTF-8 encoded
218  if (calculated >= 0xD800 && calculated <= 0xDFFF)
219  return REPLACEMENT_CHARACTER;
220  // oversized encoded characters are invalid
221  return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
222  }
223 
224  if (firstByte < 0xF8) {
225  if (e - s < 4)
226  return REPLACEMENT_CHARACTER;
227 
228  unsigned int calculated = ((firstByte & 0x07) << 18) |
229  ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
230  ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
231  (static_cast<unsigned int>(s[3]) & 0x3F);
232  s += 3;
233  // oversized encoded characters are invalid
234  return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
235  }
236 
237  return REPLACEMENT_CHARACTER;
238 }
239 
240 static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
241  "101112131415161718191a1b1c1d1e1f"
242  "202122232425262728292a2b2c2d2e2f"
243  "303132333435363738393a3b3c3d3e3f"
244  "404142434445464748494a4b4c4d4e4f"
245  "505152535455565758595a5b5c5d5e5f"
246  "606162636465666768696a6b6c6d6e6f"
247  "707172737475767778797a7b7c7d7e7f"
248  "808182838485868788898a8b8c8d8e8f"
249  "909192939495969798999a9b9c9d9e9f"
250  "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
251  "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
252  "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
253  "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
254  "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
255  "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
256 
257 static String toHex16Bit(unsigned int x) {
258  const unsigned int hi = (x >> 8) & 0xff;
259  const unsigned int lo = x & 0xff;
260  String result(4, ' ');
261  result[0] = hex2[2 * hi];
262  result[1] = hex2[2 * hi + 1];
263  result[2] = hex2[2 * lo];
264  result[3] = hex2[2 * lo + 1];
265  return result;
266 }
267 
268 static String valueToQuotedStringN(const char* value, unsigned length,
269  bool emitUTF8 = false) {
270  if (value == nullptr)
271  return "";
272 
273  if (!isAnyCharRequiredQuoting(value, length))
274  return String("\"") + value + "\"";
275  // We have to walk value and escape any special characters.
276  // Appending to String is not efficient, but this should be rare.
277  // (Note: forward slashes are *not* rare, but I am not escaping them.)
278  String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
279  String result;
280  result.reserve(maxsize); // to avoid lots of mallocs
281  result += "\"";
282  char const* end = value + length;
283  for (const char* c = value; c != end; ++c) {
284  switch (*c) {
285  case '\"':
286  result += "\\\"";
287  break;
288  case '\\':
289  result += "\\\\";
290  break;
291  case '\b':
292  result += "\\b";
293  break;
294  case '\f':
295  result += "\\f";
296  break;
297  case '\n':
298  result += "\\n";
299  break;
300  case '\r':
301  result += "\\r";
302  break;
303  case '\t':
304  result += "\\t";
305  break;
306  // case '/':
307  // Even though \/ is considered a legal escape in JSON, a bare
308  // slash is also legal, so I see no reason to escape it.
309  // (I hope I am not misunderstanding something.)
310  // blep notes: actually escaping \/ may be useful in javascript to avoid </
311  // sequence.
312  // Should add a flag to allow this compatibility mode and prevent this
313  // sequence from occurring.
314  default: {
315  if (emitUTF8) {
316  result += *c;
317  } else {
318  unsigned int codepoint = utf8ToCodepoint(c, end);
319  const unsigned int FIRST_NON_CONTROL_CODEPOINT = 0x20;
320  const unsigned int LAST_NON_CONTROL_CODEPOINT = 0x7F;
321  const unsigned int FIRST_SURROGATE_PAIR_CODEPOINT = 0x10000;
322  // don't escape non-control characters
323  // (short escape sequence are applied above)
324  if (FIRST_NON_CONTROL_CODEPOINT <= codepoint &&
325  codepoint <= LAST_NON_CONTROL_CODEPOINT) {
326  result += static_cast<char>(codepoint);
327  } else if (codepoint <
328  FIRST_SURROGATE_PAIR_CODEPOINT) { // codepoint is in Basic
329  // Multilingual Plane
330  result += "\\u";
331  result += toHex16Bit(codepoint);
332  } else { // codepoint is not in Basic Multilingual Plane
333  // convert to surrogate pair first
334  codepoint -= FIRST_SURROGATE_PAIR_CODEPOINT;
335  result += "\\u";
336  result += toHex16Bit((codepoint >> 10) + 0xD800);
337  result += "\\u";
338  result += toHex16Bit((codepoint & 0x3FF) + 0xDC00);
339  }
340  }
341  } break;
342  }
343  }
344  result += "\"";
345  return result;
346 }
347 
348 String valueToQuotedString(const char* value) {
349  return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
350 }
351 
352 // Class Writer
353 // //////////////////////////////////////////////////////////////////
354 Writer::~Writer() = default;
355 
356 // Class FastWriter
357 // //////////////////////////////////////////////////////////////////
358 
359 FastWriter::FastWriter()
360 
361  = default;
362 
363 void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
364 
365 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
366 
367 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
368 
369 String FastWriter::write(const Value& root) {
370  document_.clear();
371  writeValue(root);
372  if (!omitEndingLineFeed_)
373  document_ += '\n';
374  return document_;
375 }
376 
377 void FastWriter::writeValue(const Value& value) {
378  switch (value.type()) {
379  case nullValue:
380  if (!dropNullPlaceholders_)
381  document_ += "null";
382  break;
383  case intValue:
384  document_ += valueToString(value.asLargestInt());
385  break;
386  case uintValue:
387  document_ += valueToString(value.asLargestUInt());
388  break;
389  case realValue:
390  document_ += valueToString(value.asDouble());
391  break;
392  case stringValue: {
393  // Is NULL possible for value.string_? No.
394  char const* str;
395  char const* end;
396  bool ok = value.getString(&str, &end);
397  if (ok)
398  document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
399  break;
400  }
401  case booleanValue:
402  document_ += valueToString(value.asBool());
403  break;
404  case arrayValue: {
405  document_ += '[';
406  ArrayIndex size = value.size();
407  for (ArrayIndex index = 0; index < size; ++index) {
408  if (index > 0)
409  document_ += ',';
410  writeValue(value[index]);
411  }
412  document_ += ']';
413  } break;
414  case objectValue: {
415  Value::Members members(value.getMemberNames());
416  document_ += '{';
417  for (auto it = members.begin(); it != members.end(); ++it) {
418  const String& name = *it;
419  if (it != members.begin())
420  document_ += ',';
421  document_ += valueToQuotedStringN(name.data(),
422  static_cast<unsigned>(name.length()));
423  document_ += yamlCompatibilityEnabled_ ? ": " : ":";
424  writeValue(value[name]);
425  }
426  document_ += '}';
427  } break;
428  }
429 }
430 
431 // Class StyledWriter
432 // //////////////////////////////////////////////////////////////////
433 
434 StyledWriter::StyledWriter() = default;
435 
436 String StyledWriter::write(const Value& root) {
437  document_.clear();
438  addChildValues_ = false;
439  indentString_.clear();
440  writeCommentBeforeValue(root);
441  writeValue(root);
442  writeCommentAfterValueOnSameLine(root);
443  document_ += '\n';
444  return document_;
445 }
446 
447 void StyledWriter::writeValue(const Value& value) {
448  switch (value.type()) {
449  case nullValue:
450  pushValue("null");
451  break;
452  case intValue:
453  pushValue(valueToString(value.asLargestInt()));
454  break;
455  case uintValue:
456  pushValue(valueToString(value.asLargestUInt()));
457  break;
458  case realValue:
459  pushValue(valueToString(value.asDouble()));
460  break;
461  case stringValue: {
462  // Is NULL possible for value.string_? No.
463  char const* str;
464  char const* end;
465  bool ok = value.getString(&str, &end);
466  if (ok)
467  pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
468  else
469  pushValue("");
470  break;
471  }
472  case booleanValue:
473  pushValue(valueToString(value.asBool()));
474  break;
475  case arrayValue:
476  writeArrayValue(value);
477  break;
478  case objectValue: {
479  Value::Members members(value.getMemberNames());
480  if (members.empty())
481  pushValue("{}");
482  else {
483  writeWithIndent("{");
484  indent();
485  auto it = members.begin();
486  for (;;) {
487  const String& name = *it;
488  const Value& childValue = value[name];
489  writeCommentBeforeValue(childValue);
490  writeWithIndent(valueToQuotedString(name.c_str()));
491  document_ += " : ";
492  writeValue(childValue);
493  if (++it == members.end()) {
494  writeCommentAfterValueOnSameLine(childValue);
495  break;
496  }
497  document_ += ',';
498  writeCommentAfterValueOnSameLine(childValue);
499  }
500  unindent();
501  writeWithIndent("}");
502  }
503  } break;
504  }
505 }
506 
507 void StyledWriter::writeArrayValue(const Value& value) {
508  unsigned size = value.size();
509  if (size == 0)
510  pushValue("[]");
511  else {
512  bool isArrayMultiLine = isMultilineArray(value);
513  if (isArrayMultiLine) {
514  writeWithIndent("[");
515  indent();
516  bool hasChildValue = !childValues_.empty();
517  unsigned index = 0;
518  for (;;) {
519  const Value& childValue = value[index];
520  writeCommentBeforeValue(childValue);
521  if (hasChildValue)
522  writeWithIndent(childValues_[index]);
523  else {
524  writeIndent();
525  writeValue(childValue);
526  }
527  if (++index == size) {
528  writeCommentAfterValueOnSameLine(childValue);
529  break;
530  }
531  document_ += ',';
532  writeCommentAfterValueOnSameLine(childValue);
533  }
534  unindent();
535  writeWithIndent("]");
536  } else // output on a single line
537  {
538  assert(childValues_.size() == size);
539  document_ += "[ ";
540  for (unsigned index = 0; index < size; ++index) {
541  if (index > 0)
542  document_ += ", ";
543  document_ += childValues_[index];
544  }
545  document_ += " ]";
546  }
547  }
548 }
549 
550 bool StyledWriter::isMultilineArray(const Value& value) {
551  ArrayIndex const size = value.size();
552  bool isMultiLine = size * 3 >= rightMargin_;
553  childValues_.clear();
554  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
555  const Value& childValue = value[index];
556  isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
557  !childValue.empty());
558  }
559  if (!isMultiLine) // check if line length > max line length
560  {
561  childValues_.reserve(size);
562  addChildValues_ = true;
563  ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
564  for (ArrayIndex index = 0; index < size; ++index) {
565  if (hasCommentForValue(value[index])) {
566  isMultiLine = true;
567  }
568  writeValue(value[index]);
569  lineLength += static_cast<ArrayIndex>(childValues_[index].length());
570  }
571  addChildValues_ = false;
572  isMultiLine = isMultiLine || lineLength >= rightMargin_;
573  }
574  return isMultiLine;
575 }
576 
577 void StyledWriter::pushValue(const String& value) {
578  if (addChildValues_)
579  childValues_.push_back(value);
580  else
581  document_ += value;
582 }
583 
584 void StyledWriter::writeIndent() {
585  if (!document_.empty()) {
586  char last = document_[document_.length() - 1];
587  if (last == ' ') // already indented
588  return;
589  if (last != '\n') // Comments may add new-line
590  document_ += '\n';
591  }
592  document_ += indentString_;
593 }
594 
595 void StyledWriter::writeWithIndent(const String& value) {
596  writeIndent();
597  document_ += value;
598 }
599 
600 void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
601 
602 void StyledWriter::unindent() {
603  assert(indentString_.size() >= indentSize_);
604  indentString_.resize(indentString_.size() - indentSize_);
605 }
606 
607 void StyledWriter::writeCommentBeforeValue(const Value& root) {
608  if (!root.hasComment(commentBefore))
609  return;
610 
611  document_ += '\n';
612  writeIndent();
613  const String& comment = root.getComment(commentBefore);
614  String::const_iterator iter = comment.begin();
615  while (iter != comment.end()) {
616  document_ += *iter;
617  if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
618  writeIndent();
619  ++iter;
620  }
621 
622  // Comments are stripped of trailing newlines, so add one here
623  document_ += '\n';
624 }
625 
626 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
627  if (root.hasComment(commentAfterOnSameLine))
628  document_ += " " + root.getComment(commentAfterOnSameLine);
629 
630  if (root.hasComment(commentAfter)) {
631  document_ += '\n';
632  document_ += root.getComment(commentAfter);
633  document_ += '\n';
634  }
635 }
636 
637 bool StyledWriter::hasCommentForValue(const Value& value) {
638  return value.hasComment(commentBefore) ||
639  value.hasComment(commentAfterOnSameLine) ||
640  value.hasComment(commentAfter);
641 }
642 
643 // Class StyledStreamWriter
644 // //////////////////////////////////////////////////////////////////
645 
646 StyledStreamWriter::StyledStreamWriter(String indentation)
647  : document_(nullptr), indentation_(std::move(indentation)),
648  addChildValues_(), indented_(false) {}
649 
650 void StyledStreamWriter::write(OStream& out, const Value& root) {
651  document_ = &out;
652  addChildValues_ = false;
653  indentString_.clear();
654  indented_ = true;
655  writeCommentBeforeValue(root);
656  if (!indented_)
657  writeIndent();
658  indented_ = true;
659  writeValue(root);
660  writeCommentAfterValueOnSameLine(root);
661  *document_ << "\n";
662  document_ = nullptr; // Forget the stream, for safety.
663 }
664 
665 void StyledStreamWriter::writeValue(const Value& value) {
666  switch (value.type()) {
667  case nullValue:
668  pushValue("null");
669  break;
670  case intValue:
671  pushValue(valueToString(value.asLargestInt()));
672  break;
673  case uintValue:
674  pushValue(valueToString(value.asLargestUInt()));
675  break;
676  case realValue:
677  pushValue(valueToString(value.asDouble()));
678  break;
679  case stringValue: {
680  // Is NULL possible for value.string_? No.
681  char const* str;
682  char const* end;
683  bool ok = value.getString(&str, &end);
684  if (ok)
685  pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
686  else
687  pushValue("");
688  break;
689  }
690  case booleanValue:
691  pushValue(valueToString(value.asBool()));
692  break;
693  case arrayValue:
694  writeArrayValue(value);
695  break;
696  case objectValue: {
697  Value::Members members(value.getMemberNames());
698  if (members.empty())
699  pushValue("{}");
700  else {
701  writeWithIndent("{");
702  indent();
703  auto it = members.begin();
704  for (;;) {
705  const String& name = *it;
706  const Value& childValue = value[name];
707  writeCommentBeforeValue(childValue);
708  writeWithIndent(valueToQuotedString(name.c_str()));
709  *document_ << " : ";
710  writeValue(childValue);
711  if (++it == members.end()) {
712  writeCommentAfterValueOnSameLine(childValue);
713  break;
714  }
715  *document_ << ",";
716  writeCommentAfterValueOnSameLine(childValue);
717  }
718  unindent();
719  writeWithIndent("}");
720  }
721  } break;
722  }
723 }
724 
725 void StyledStreamWriter::writeArrayValue(const Value& value) {
726  unsigned size = value.size();
727  if (size == 0)
728  pushValue("[]");
729  else {
730  bool isArrayMultiLine = isMultilineArray(value);
731  if (isArrayMultiLine) {
732  writeWithIndent("[");
733  indent();
734  bool hasChildValue = !childValues_.empty();
735  unsigned index = 0;
736  for (;;) {
737  const Value& childValue = value[index];
738  writeCommentBeforeValue(childValue);
739  if (hasChildValue)
740  writeWithIndent(childValues_[index]);
741  else {
742  if (!indented_)
743  writeIndent();
744  indented_ = true;
745  writeValue(childValue);
746  indented_ = false;
747  }
748  if (++index == size) {
749  writeCommentAfterValueOnSameLine(childValue);
750  break;
751  }
752  *document_ << ",";
753  writeCommentAfterValueOnSameLine(childValue);
754  }
755  unindent();
756  writeWithIndent("]");
757  } else // output on a single line
758  {
759  assert(childValues_.size() == size);
760  *document_ << "[ ";
761  for (unsigned index = 0; index < size; ++index) {
762  if (index > 0)
763  *document_ << ", ";
764  *document_ << childValues_[index];
765  }
766  *document_ << " ]";
767  }
768  }
769 }
770 
771 bool StyledStreamWriter::isMultilineArray(const Value& value) {
772  ArrayIndex const size = value.size();
773  bool isMultiLine = size * 3 >= rightMargin_;
774  childValues_.clear();
775  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
776  const Value& childValue = value[index];
777  isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
778  !childValue.empty());
779  }
780  if (!isMultiLine) // check if line length > max line length
781  {
782  childValues_.reserve(size);
783  addChildValues_ = true;
784  ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
785  for (ArrayIndex index = 0; index < size; ++index) {
786  if (hasCommentForValue(value[index])) {
787  isMultiLine = true;
788  }
789  writeValue(value[index]);
790  lineLength += static_cast<ArrayIndex>(childValues_[index].length());
791  }
792  addChildValues_ = false;
793  isMultiLine = isMultiLine || lineLength >= rightMargin_;
794  }
795  return isMultiLine;
796 }
797 
798 void StyledStreamWriter::pushValue(const String& value) {
799  if (addChildValues_)
800  childValues_.push_back(value);
801  else
802  *document_ << value;
803 }
804 
805 void StyledStreamWriter::writeIndent() {
806  // blep intended this to look at the so-far-written string
807  // to determine whether we are already indented, but
808  // with a stream we cannot do that. So we rely on some saved state.
809  // The caller checks indented_.
810  *document_ << '\n' << indentString_;
811 }
812 
813 void StyledStreamWriter::writeWithIndent(const String& value) {
814  if (!indented_)
815  writeIndent();
816  *document_ << value;
817  indented_ = false;
818 }
819 
820 void StyledStreamWriter::indent() { indentString_ += indentation_; }
821 
822 void StyledStreamWriter::unindent() {
823  assert(indentString_.size() >= indentation_.size());
824  indentString_.resize(indentString_.size() - indentation_.size());
825 }
826 
827 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
828  if (!root.hasComment(commentBefore))
829  return;
830 
831  if (!indented_)
832  writeIndent();
833  const String& comment = root.getComment(commentBefore);
834  String::const_iterator iter = comment.begin();
835  while (iter != comment.end()) {
836  *document_ << *iter;
837  if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
838  // writeIndent(); // would include newline
839  *document_ << indentString_;
840  ++iter;
841  }
842  indented_ = false;
843 }
844 
845 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
846  if (root.hasComment(commentAfterOnSameLine))
847  *document_ << ' ' << root.getComment(commentAfterOnSameLine);
848 
849  if (root.hasComment(commentAfter)) {
850  writeIndent();
851  *document_ << root.getComment(commentAfter);
852  }
853  indented_ = false;
854 }
855 
856 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
857  return value.hasComment(commentBefore) ||
858  value.hasComment(commentAfterOnSameLine) ||
859  value.hasComment(commentAfter);
860 }
861 
863 // BuiltStyledStreamWriter
864 
866 struct CommentStyle {
868  enum Enum {
869  None,
870  Most,
871  All
872  };
873 };
874 
875 struct BuiltStyledStreamWriter : public StreamWriter {
876  BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
877  String colonSymbol, String nullSymbol,
878  String endingLineFeedSymbol, bool useSpecialFloats,
879  bool emitUTF8, unsigned int precision,
880  PrecisionType precisionType);
881  int write(Value const& root, OStream* sout) override;
882 
883 private:
884  void writeValue(Value const& value);
885  void writeArrayValue(Value const& value);
886  bool isMultilineArray(Value const& value);
887  void pushValue(String const& value);
888  void writeIndent();
889  void writeWithIndent(String const& value);
890  void indent();
891  void unindent();
892  void writeCommentBeforeValue(Value const& root);
893  void writeCommentAfterValueOnSameLine(Value const& root);
894  static bool hasCommentForValue(const Value& value);
895 
896  using ChildValues = std::vector<String>;
897 
898  ChildValues childValues_;
899  String indentString_;
900  unsigned int rightMargin_;
901  String indentation_;
902  CommentStyle::Enum cs_;
903  String colonSymbol_;
904  String nullSymbol_;
905  String endingLineFeedSymbol_;
906  bool addChildValues_ : 1;
907  bool indented_ : 1;
908  bool useSpecialFloats_ : 1;
909  bool emitUTF8_ : 1;
910  unsigned int precision_;
911  PrecisionType precisionType_;
912 };
913 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
914  String indentation, CommentStyle::Enum cs, String colonSymbol,
915  String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
916  bool emitUTF8, unsigned int precision, PrecisionType precisionType)
917  : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
918  colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
919  endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
920  addChildValues_(false), indented_(false),
921  useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
922  precision_(precision), precisionType_(precisionType) {}
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_ = nullptr;
936  return 0;
937 }
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  auto 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 
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 
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 
1078 void BuiltStyledStreamWriter::pushValue(String const& value) {
1079  if (addChildValues_)
1080  childValues_.push_back(value);
1081  else
1082  *sout_ << value;
1083 }
1084 
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 
1097 void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1098  if (!indented_)
1099  writeIndent();
1100  *sout_ << value;
1101  indented_ = false;
1102 }
1103 
1104 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1105 
1106 void BuiltStyledStreamWriter::unindent() {
1107  assert(indentString_.size() >= indentation_.size());
1108  indentString_.resize(indentString_.size() - indentation_.size());
1109 }
1110 
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 
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
1145 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1146  return value.hasComment(commentBefore) ||
1147  value.hasComment(commentAfterOnSameLine) ||
1148  value.hasComment(commentAfter);
1149 }
1150 
1152 // StreamWriter
1153 
1154 StreamWriter::StreamWriter() : sout_(nullptr) {}
1155 StreamWriter::~StreamWriter() = default;
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 = PrecisionType::significantDigits;
1179  } else if (pt_str == "decimal") {
1180  precisionType = 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 }
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 }
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);
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 }
1230  return settings_[key];
1231 }
1232 // static
1235  (*settings)["commentStyle"] = "All";
1236  (*settings)["indentation"] = "\t";
1237  (*settings)["enableYAMLCompatibility"] = false;
1238  (*settings)["dropNullPlaceholders"] = false;
1239  (*settings)["useSpecialFloats"] = false;
1240  (*settings)["emitUTF8"] = false;
1241  (*settings)["precision"] = 17;
1242  (*settings)["precisionType"] = "significant";
1244 }
1245 
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  return sout.str();
1251 }
1252 
1253 OStream& operator<<(OStream& sout, Value const& root) {
1254  StreamWriterBuilder builder;
1255  StreamWriterPtr const writer(builder.newStreamWriter());
1256  writer->write(root, &sout);
1257  return sout;
1258 }
1259 
1260 } // namespace Json
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:688
Json::StreamWriter::Factory::newStreamWriter
virtual StreamWriter * newStreamWriter() const =0
Allocate a CharReader via operator new().
Json::valueToString
String valueToString(Int value)
Definition: json_writer.cpp:118
Json::StreamWriter
Usage:
Definition: writer.h:41
Json::ArrayIndex
unsigned int ArrayIndex
Definition: forwards.h:29
Json::OStringStream
std::basic_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
Definition: config.h:168
Json::uintValue
unsigned integer value
Definition: value.h:100
Json::significantDigits
we set max number of significant digits in string
Definition: value.h:119
Json::LargestUInt
UInt64 LargestUInt
Definition: config.h:154
isnan
#define isnan
Definition: json_writer.cpp:37
Json::commentAfterOnSameLine
a comment just after a value on the same line
Definition: value.h:110
Json::Int
int Int
Definition: config.h:138
Json::StreamWriter::Factory
A simple abstract factory.
Definition: writer.h:58
Json::commentBefore
a comment placed on the line before a value
Definition: value.h:109
Json::stringValue
UTF-8 string value.
Definition: value.h:102
Json::uintToString
static void uintToString(LargestUInt value, char *&current)
Converts an unsigned integer to string.
Definition: json_tool.h:81
Json::StreamWriterBuilder::~StreamWriterBuilder
~StreamWriterBuilder() override
Json::LargestInt
Int64 LargestInt
Definition: config.h:153
Json::UInt
unsigned int UInt
Definition: config.h:139
Json::commentAfter
a comment on the line after a value (only make sense for
Definition: value.h:111
Json::intValue
signed integer value
Definition: value.h:99
Json::Value::empty
bool empty() const
Return true if empty array, empty object, or null; otherwise, false.
Definition: json_value.cpp:894
jsoncpp_snprintf
#define jsoncpp_snprintf
Definition: config.h:79
Json::decimalPlaces
we set max number of digits after "." in string
Definition: value.h:120
Json::valueToQuotedStringN
static String valueToQuotedStringN(const char *value, unsigned length, bool emitUTF8=false)
Definition: json_writer.cpp:268
Json::StreamWriterPtr
std::auto_ptr< StreamWriter > StreamWriterPtr
Definition: json_writer.cpp:89
Json::StreamWriterBuilder::validate
bool validate(Json::Value *invalid) const
Definition: json_writer.cpp:1212
Json::StreamWriter::~StreamWriter
virtual ~StreamWriter()
Json::StreamWriterBuilder::setDefaults
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
Definition: json_writer.cpp:1233
json_tool.h
Json::fixNumericLocale
Iter fixNumericLocale(Iter begin, Iter end)
Change ',' to '.
Definition: json_tool.h:94
Json::operator<<
OStream & operator<<(OStream &, const Value &root)
Output using the StyledStreamWriter.
Definition: json_writer.cpp:1253
Json::StreamWriterBuilder::operator[]
Value & operator[](const String &key)
A simple way to update a specific setting.
Definition: json_writer.cpp:1229
Json::utf8ToCodepoint
static unsigned int utf8ToCodepoint(const char *&s, const char *e)
Definition: json_writer.cpp:189
Json::arrayValue
array value (ordered list)
Definition: value.h:104
Json::UIntToStringBuffer
char UIntToStringBuffer[uintToStringBufferSize]
Definition: json_tool.h:74
Json::getValidWriterKeys
static void getValidWriterKeys(std::set< String > *valid_keys)
Definition: json_writer.cpp:1201
Json::PrecisionType
PrecisionType
Type of precision for formatting of real values.
Definition: value.h:118
Json::Value::minLargestInt
static constexpr LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
Definition: value.h:218
Json::Value
Represents a JSON value.
Definition: value.h:188
Json::StreamWriterBuilder::settings_
Json::Value settings_
Configuration of this builder.
Definition: writer.h:119
Json::OStream
std::ostream OStream
Definition: config.h:170
Json
JSON (JavaScript Object Notation).
Definition: allocator.h:14
Json::Value::getMemberNames
Members getMemberNames() const
Return a list of the member names.
Definition: json_value.cpp:1267
Json::Value::asString
String asString() const
Embedded zeroes are possible.
Definition: json_value.cpp:631
isfinite
#define isfinite
Definition: json_writer.cpp:42
Json::hex2
static const char hex2[]
Definition: json_writer.cpp:240
Json::objectValue
object value (collection of name/value pairs).
Definition: value.h:105
Json::StreamWriterBuilder::StreamWriterBuilder
StreamWriterBuilder()
Definition: json_writer.cpp:1157
Json::String
std::basic_string< char, std::char_traits< char >, Allocator< char > > String
Definition: config.h:162
Json::valueToQuotedString
String valueToQuotedString(const char *value)
Definition: json_writer.cpp:348
Json::writeString
String writeString(StreamWriter::Factory const &factory, Value const &root)
Write into stringstream, then return string, for convenience.
Definition: json_writer.cpp:1246
Json::StreamWriterBuilder
Build a StreamWriter implementation.
Definition: writer.h:89
Json::booleanValue
bool value
Definition: value.h:103
Json::nullValue
'null' value
Definition: value.h:98
Json::toHex16Bit
static String toHex16Bit(unsigned int x)
Definition: json_writer.cpp:257
Json::StreamWriter::Factory::~Factory
virtual ~Factory()
Json::StreamWriterBuilder::newStreamWriter
StreamWriter * newStreamWriter() const override
Definition: json_writer.cpp:1159
Json::fixZerosInTheEnd
Iter fixZerosInTheEnd(Iter begin, Iter end)
Return iterator that would be the new end of the range [begin,end), if we were to delete zeros in the...
Definition: json_tool.h:119
Json::realValue
double value
Definition: value.h:101
Json::isAnyCharRequiredQuoting
static bool isAnyCharRequiredQuoting(char const *s, size_t n)
Definition: json_writer.cpp:176
Json::Value::Members
std::vector< String > Members
Definition: value.h:192
Json::Value::maxLargestInt
static constexpr LargestInt maxLargestInt
Maximum signed integer value that can be stored in a Json::Value.
Definition: value.h:221
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:816
writer.h