JsonCpp project page JsonCpp home page

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