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