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