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
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include <json/writer.h>
9 #endif // if !defined(JSON_IS_AMALGAMATION)
19 #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
21 #define isfinite _finite
22 #elif defined(__sun) && defined(__SVR4) //Solaris
23 #if !defined(isfinite)
25 #define isfinite finite
28 #if !defined(isfinite)
30 #define isfinite finite
33 #if !defined(isfinite)
34 #if defined(__ia64) && !defined(finite)
35 #define isfinite(x) ((sizeof(x) == sizeof(float) ? \
36 _Isfinitef(x) : _IsFinite(x)))
39 #define isfinite finite
44 #if !(defined(__QNXNTO__)) // QNX already defines isfinite
45 #define isfinite std::isfinite
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
55 #define snprintf _snprintf
57 #elif defined(__ANDROID__) || defined(__QNXNTO__)
58 #define snprintf snprintf
59 #elif __cplusplus >= 201103L
60 #if !defined(__MINGW32__) && !defined(__CYGWIN__)
61 #define snprintf std::snprintf
65 #if defined(__BORLANDC__)
67 #define isfinite _finite
68 #define snprintf _snprintf
71 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
72 // Disable warning about strdup being deprecated.
73 #pragma warning(disable : 4996)
78 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
79 typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
81 typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
84 static bool containsControlCharacter(const char* str) {
86 if (isControlCharacter(*(str++)))
92 static bool containsControlCharacter0(const char* str, unsigned len) {
93 char const* end = str + len;
95 if (isControlCharacter(*str) || 0==*str)
102 JSONCPP_STRING valueToString(LargestInt value) {
103 UIntToStringBuffer buffer;
104 char* current = buffer + sizeof(buffer);
105 if (value == Value::minLargestInt) {
106 uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
108 } else if (value < 0) {
109 uintToString(LargestUInt(-value), current);
112 uintToString(LargestUInt(value), current);
114 assert(current >= buffer);
118 JSONCPP_STRING valueToString(LargestUInt value) {
119 UIntToStringBuffer buffer;
120 char* current = buffer + sizeof(buffer);
121 uintToString(value, current);
122 assert(current >= buffer);
126 #if defined(JSON_HAS_INT64)
128 JSONCPP_STRING valueToString(Int value) {
129 return valueToString(LargestInt(value));
132 JSONCPP_STRING valueToString(UInt value) {
133 return valueToString(LargestUInt(value));
136 #endif // # if defined(JSON_HAS_INT64)
139 JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
140 // Allocate a buffer that is more than large enough to store the 16 digits of
141 // precision requested below.
145 char formatString[15];
146 snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
148 // Print into the buffer. We need not request the alternative representation
149 // that always has a decimal point because JSON doesn't distingish the
150 // concepts of reals and integers.
151 if (isfinite(value)) {
152 len = snprintf(buffer, sizeof(buffer), formatString, value);
153 fixNumericLocale(buffer, buffer + len);
155 // try to ensure we preserve the fact that this was given to us as a double on input
156 if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
157 strcat(buffer, ".0");
161 // IEEE standard states that NaN values will not compare to themselves
162 if (value != value) {
163 len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
164 } else if (value < 0) {
165 len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
167 len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
175 JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
177 JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
179 JSONCPP_STRING valueToQuotedString(const char* value) {
182 // Not sure how to handle unicode...
183 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
184 !containsControlCharacter(value))
185 return JSONCPP_STRING("\"") + value + "\"";
186 // We have to walk value and escape any special characters.
187 // Appending to JSONCPP_STRING is not efficient, but this should be rare.
188 // (Note: forward slashes are *not* rare, but I am not escaping them.)
189 JSONCPP_STRING::size_type maxsize =
190 strlen(value) * 2 + 3; // allescaped+quotes+NULL
191 JSONCPP_STRING result;
192 result.reserve(maxsize); // to avoid lots of mallocs
194 for (const char* c = value; *c != 0; ++c) {
218 // Even though \/ is considered a legal escape in JSON, a bare
219 // slash is also legal, so I see no reason to escape it.
220 // (I hope I am not misunderstanding something.
221 // blep notes: actually escaping \/ may be useful in javascript to avoid </
223 // Should add a flag to allow this compatibility mode and prevent this
224 // sequence from occurring.
226 if (isControlCharacter(*c)) {
227 JSONCPP_OSTRINGSTREAM oss;
228 oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
229 << std::setw(4) << static_cast<int>(*c);
241 // https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
242 static char const* strnpbrk(char const* s, char const* accept, size_t n) {
243 assert((s || !n) && accept);
245 char const* const end = s + n;
246 for (char const* cur = s; cur < end; ++cur) {
248 for (char const* a = accept; *a; ++a) {
256 static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {
259 // Not sure how to handle unicode...
260 if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
261 !containsControlCharacter0(value, length))
262 return JSONCPP_STRING("\"") + value + "\"";
263 // We have to walk value and escape any special characters.
264 // Appending to JSONCPP_STRING is not efficient, but this should be rare.
265 // (Note: forward slashes are *not* rare, but I am not escaping them.)
266 JSONCPP_STRING::size_type maxsize =
267 length * 2 + 3; // allescaped+quotes+NULL
268 JSONCPP_STRING result;
269 result.reserve(maxsize); // to avoid lots of mallocs
271 char const* end = value + length;
272 for (const char* c = value; c != end; ++c) {
296 // Even though \/ is considered a legal escape in JSON, a bare
297 // slash is also legal, so I see no reason to escape it.
298 // (I hope I am not misunderstanding something.)
299 // blep notes: actually escaping \/ may be useful in javascript to avoid </
301 // Should add a flag to allow this compatibility mode and prevent this
302 // sequence from occurring.
304 if ((isControlCharacter(*c)) || (*c == 0)) {
305 JSONCPP_OSTRINGSTREAM oss;
306 oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
307 << std::setw(4) << static_cast<int>(*c);
320 // //////////////////////////////////////////////////////////////////
324 // //////////////////////////////////////////////////////////////////
326 FastWriter::FastWriter()
327 : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
328 omitEndingLineFeed_(false) {}
330 void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
332 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
334 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
336 JSONCPP_STRING FastWriter::write(const Value& root) {
339 if (!omitEndingLineFeed_)
344 void FastWriter::writeValue(const Value& value) {
345 switch (value.type()) {
347 if (!dropNullPlaceholders_)
351 document_ += valueToString(value.asLargestInt());
354 document_ += valueToString(value.asLargestUInt());
357 document_ += valueToString(value.asDouble());
361 // Is NULL possible for value.string_? No.
364 bool ok = value.getString(&str, &end);
365 if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
369 document_ += valueToString(value.asBool());
373 ArrayIndex size = value.size();
374 for (ArrayIndex index = 0; index < size; ++index) {
377 writeValue(value[index]);
382 Value::Members members(value.getMemberNames());
384 for (Value::Members::iterator it = members.begin(); it != members.end();
386 const JSONCPP_STRING& name = *it;
387 if (it != members.begin())
389 document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
390 document_ += yamlCompatiblityEnabled_ ? ": " : ":";
391 writeValue(value[name]);
398 // Class StyledWriter
399 // //////////////////////////////////////////////////////////////////
401 StyledWriter::StyledWriter()
402 : rightMargin_(74), indentSize_(3), addChildValues_() {}
404 JSONCPP_STRING StyledWriter::write(const Value& root) {
406 addChildValues_ = false;
407 indentString_.clear();
408 writeCommentBeforeValue(root);
410 writeCommentAfterValueOnSameLine(root);
415 void StyledWriter::writeValue(const Value& value) {
416 switch (value.type()) {
421 pushValue(valueToString(value.asLargestInt()));
424 pushValue(valueToString(value.asLargestUInt()));
427 pushValue(valueToString(value.asDouble()));
431 // Is NULL possible for value.string_? No.
434 bool ok = value.getString(&str, &end);
435 if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
440 pushValue(valueToString(value.asBool()));
443 writeArrayValue(value);
446 Value::Members members(value.getMemberNames());
450 writeWithIndent("{");
452 Value::Members::iterator it = members.begin();
454 const JSONCPP_STRING& name = *it;
455 const Value& childValue = value[name];
456 writeCommentBeforeValue(childValue);
457 writeWithIndent(valueToQuotedString(name.c_str()));
459 writeValue(childValue);
460 if (++it == members.end()) {
461 writeCommentAfterValueOnSameLine(childValue);
465 writeCommentAfterValueOnSameLine(childValue);
468 writeWithIndent("}");
474 void StyledWriter::writeArrayValue(const Value& value) {
475 unsigned size = value.size();
479 bool isArrayMultiLine = isMultineArray(value);
480 if (isArrayMultiLine) {
481 writeWithIndent("[");
483 bool hasChildValue = !childValues_.empty();
486 const Value& childValue = value[index];
487 writeCommentBeforeValue(childValue);
489 writeWithIndent(childValues_[index]);
492 writeValue(childValue);
494 if (++index == size) {
495 writeCommentAfterValueOnSameLine(childValue);
499 writeCommentAfterValueOnSameLine(childValue);
502 writeWithIndent("]");
503 } else // output on a single line
505 assert(childValues_.size() == size);
507 for (unsigned index = 0; index < size; ++index) {
510 document_ += childValues_[index];
517 bool StyledWriter::isMultineArray(const Value& value) {
518 ArrayIndex const size = value.size();
519 bool isMultiLine = size * 3 >= rightMargin_;
520 childValues_.clear();
521 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
522 const Value& childValue = value[index];
523 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
524 childValue.size() > 0);
526 if (!isMultiLine) // check if line length > max line length
528 childValues_.reserve(size);
529 addChildValues_ = true;
530 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
531 for (ArrayIndex index = 0; index < size; ++index) {
532 if (hasCommentForValue(value[index])) {
535 writeValue(value[index]);
536 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
538 addChildValues_ = false;
539 isMultiLine = isMultiLine || lineLength >= rightMargin_;
544 void StyledWriter::pushValue(const JSONCPP_STRING& value) {
546 childValues_.push_back(value);
551 void StyledWriter::writeIndent() {
552 if (!document_.empty()) {
553 char last = document_[document_.length() - 1];
554 if (last == ' ') // already indented
556 if (last != '\n') // Comments may add new-line
559 document_ += indentString_;
562 void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {
567 void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }
569 void StyledWriter::unindent() {
570 assert(indentString_.size() >= indentSize_);
571 indentString_.resize(indentString_.size() - indentSize_);
574 void StyledWriter::writeCommentBeforeValue(const Value& root) {
575 if (!root.hasComment(commentBefore))
580 const JSONCPP_STRING& comment = root.getComment(commentBefore);
581 JSONCPP_STRING::const_iterator iter = comment.begin();
582 while (iter != comment.end()) {
585 (iter != comment.end() && *(iter + 1) == '/'))
590 // Comments are stripped of trailing newlines, so add one here
594 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
595 if (root.hasComment(commentAfterOnSameLine))
596 document_ += " " + root.getComment(commentAfterOnSameLine);
598 if (root.hasComment(commentAfter)) {
600 document_ += root.getComment(commentAfter);
605 bool StyledWriter::hasCommentForValue(const Value& value) {
606 return value.hasComment(commentBefore) ||
607 value.hasComment(commentAfterOnSameLine) ||
608 value.hasComment(commentAfter);
611 // Class StyledStreamWriter
612 // //////////////////////////////////////////////////////////////////
614 StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)
615 : document_(NULL), rightMargin_(74), indentation_(indentation),
618 void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {
620 addChildValues_ = false;
621 indentString_.clear();
623 writeCommentBeforeValue(root);
624 if (!indented_) writeIndent();
627 writeCommentAfterValueOnSameLine(root);
629 document_ = NULL; // Forget the stream, for safety.
632 void StyledStreamWriter::writeValue(const Value& value) {
633 switch (value.type()) {
638 pushValue(valueToString(value.asLargestInt()));
641 pushValue(valueToString(value.asLargestUInt()));
644 pushValue(valueToString(value.asDouble()));
648 // Is NULL possible for value.string_? No.
651 bool ok = value.getString(&str, &end);
652 if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
657 pushValue(valueToString(value.asBool()));
660 writeArrayValue(value);
663 Value::Members members(value.getMemberNames());
667 writeWithIndent("{");
669 Value::Members::iterator it = members.begin();
671 const JSONCPP_STRING& name = *it;
672 const Value& childValue = value[name];
673 writeCommentBeforeValue(childValue);
674 writeWithIndent(valueToQuotedString(name.c_str()));
676 writeValue(childValue);
677 if (++it == members.end()) {
678 writeCommentAfterValueOnSameLine(childValue);
682 writeCommentAfterValueOnSameLine(childValue);
685 writeWithIndent("}");
691 void StyledStreamWriter::writeArrayValue(const Value& value) {
692 unsigned size = value.size();
696 bool isArrayMultiLine = isMultineArray(value);
697 if (isArrayMultiLine) {
698 writeWithIndent("[");
700 bool hasChildValue = !childValues_.empty();
703 const Value& childValue = value[index];
704 writeCommentBeforeValue(childValue);
706 writeWithIndent(childValues_[index]);
708 if (!indented_) writeIndent();
710 writeValue(childValue);
713 if (++index == size) {
714 writeCommentAfterValueOnSameLine(childValue);
718 writeCommentAfterValueOnSameLine(childValue);
721 writeWithIndent("]");
722 } else // output on a single line
724 assert(childValues_.size() == size);
726 for (unsigned index = 0; index < size; ++index) {
729 *document_ << childValues_[index];
736 bool StyledStreamWriter::isMultineArray(const Value& value) {
737 ArrayIndex const size = value.size();
738 bool isMultiLine = size * 3 >= rightMargin_;
739 childValues_.clear();
740 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
741 const Value& childValue = value[index];
742 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
743 childValue.size() > 0);
745 if (!isMultiLine) // check if line length > max line length
747 childValues_.reserve(size);
748 addChildValues_ = true;
749 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
750 for (ArrayIndex index = 0; index < size; ++index) {
751 if (hasCommentForValue(value[index])) {
754 writeValue(value[index]);
755 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
757 addChildValues_ = false;
758 isMultiLine = isMultiLine || lineLength >= rightMargin_;
763 void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {
765 childValues_.push_back(value);
770 void StyledStreamWriter::writeIndent() {
771 // blep intended this to look at the so-far-written string
772 // to determine whether we are already indented, but
773 // with a stream we cannot do that. So we rely on some saved state.
774 // The caller checks indented_.
775 *document_ << '\n' << indentString_;
778 void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {
779 if (!indented_) writeIndent();
784 void StyledStreamWriter::indent() { indentString_ += indentation_; }
786 void StyledStreamWriter::unindent() {
787 assert(indentString_.size() >= indentation_.size());
788 indentString_.resize(indentString_.size() - indentation_.size());
791 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
792 if (!root.hasComment(commentBefore))
795 if (!indented_) writeIndent();
796 const JSONCPP_STRING& comment = root.getComment(commentBefore);
797 JSONCPP_STRING::const_iterator iter = comment.begin();
798 while (iter != comment.end()) {
801 (iter != comment.end() && *(iter + 1) == '/'))
802 // writeIndent(); // would include newline
803 *document_ << indentString_;
809 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
810 if (root.hasComment(commentAfterOnSameLine))
811 *document_ << ' ' << root.getComment(commentAfterOnSameLine);
813 if (root.hasComment(commentAfter)) {
815 *document_ << root.getComment(commentAfter);
820 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
821 return value.hasComment(commentBefore) ||
822 value.hasComment(commentAfterOnSameLine) ||
823 value.hasComment(commentAfter);
826 //////////////////////////
827 // BuiltStyledStreamWriter
829 /// Scoped enums are not available until C++11.
830 struct CommentStyle {
831 /// Decide whether to write comments.
833 None, ///< Drop all comments.
834 Most, ///< Recover odd behavior of previous versions (not implemented yet).
835 All ///< Keep all comments.
839 struct BuiltStyledStreamWriter : public StreamWriter
841 BuiltStyledStreamWriter(
842 JSONCPP_STRING const& indentation,
843 CommentStyle::Enum cs,
844 JSONCPP_STRING const& colonSymbol,
845 JSONCPP_STRING const& nullSymbol,
846 JSONCPP_STRING const& endingLineFeedSymbol,
847 bool useSpecialFloats,
848 unsigned int precision);
849 int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
851 void writeValue(Value const& value);
852 void writeArrayValue(Value const& value);
853 bool isMultineArray(Value const& value);
854 void pushValue(JSONCPP_STRING const& value);
856 void writeWithIndent(JSONCPP_STRING const& value);
859 void writeCommentBeforeValue(Value const& root);
860 void writeCommentAfterValueOnSameLine(Value const& root);
861 static bool hasCommentForValue(const Value& value);
863 typedef std::vector<JSONCPP_STRING> ChildValues;
865 ChildValues childValues_;
866 JSONCPP_STRING indentString_;
867 unsigned int rightMargin_;
868 JSONCPP_STRING indentation_;
869 CommentStyle::Enum cs_;
870 JSONCPP_STRING colonSymbol_;
871 JSONCPP_STRING nullSymbol_;
872 JSONCPP_STRING endingLineFeedSymbol_;
873 bool addChildValues_ : 1;
875 bool useSpecialFloats_ : 1;
876 unsigned int precision_;
878 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
879 JSONCPP_STRING const& indentation,
880 CommentStyle::Enum cs,
881 JSONCPP_STRING const& colonSymbol,
882 JSONCPP_STRING const& nullSymbol,
883 JSONCPP_STRING const& endingLineFeedSymbol,
884 bool useSpecialFloats,
885 unsigned int precision)
887 , indentation_(indentation)
889 , colonSymbol_(colonSymbol)
890 , nullSymbol_(nullSymbol)
891 , endingLineFeedSymbol_(endingLineFeedSymbol)
892 , addChildValues_(false)
894 , useSpecialFloats_(useSpecialFloats)
895 , precision_(precision)
898 int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
901 addChildValues_ = false;
903 indentString_.clear();
904 writeCommentBeforeValue(root);
905 if (!indented_) writeIndent();
908 writeCommentAfterValueOnSameLine(root);
909 *sout_ << endingLineFeedSymbol_;
913 void BuiltStyledStreamWriter::writeValue(Value const& value) {
914 switch (value.type()) {
916 pushValue(nullSymbol_);
919 pushValue(valueToString(value.asLargestInt()));
922 pushValue(valueToString(value.asLargestUInt()));
925 pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
929 // Is NULL is possible for value.string_? No.
932 bool ok = value.getString(&str, &end);
933 if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
938 pushValue(valueToString(value.asBool()));
941 writeArrayValue(value);
944 Value::Members members(value.getMemberNames());
948 writeWithIndent("{");
950 Value::Members::iterator it = members.begin();
952 JSONCPP_STRING const& name = *it;
953 Value const& childValue = value[name];
954 writeCommentBeforeValue(childValue);
955 writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
956 *sout_ << colonSymbol_;
957 writeValue(childValue);
958 if (++it == members.end()) {
959 writeCommentAfterValueOnSameLine(childValue);
963 writeCommentAfterValueOnSameLine(childValue);
966 writeWithIndent("}");
972 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
973 unsigned size = value.size();
977 bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
979 writeWithIndent("[");
981 bool hasChildValue = !childValues_.empty();
984 Value const& childValue = value[index];
985 writeCommentBeforeValue(childValue);
987 writeWithIndent(childValues_[index]);
989 if (!indented_) writeIndent();
991 writeValue(childValue);
994 if (++index == size) {
995 writeCommentAfterValueOnSameLine(childValue);
999 writeCommentAfterValueOnSameLine(childValue);
1002 writeWithIndent("]");
1003 } else // output on a single line
1005 assert(childValues_.size() == size);
1007 if (!indentation_.empty()) *sout_ << " ";
1008 for (unsigned index = 0; index < size; ++index) {
1010 *sout_ << ((!indentation_.empty()) ? ", " : ",");
1011 *sout_ << childValues_[index];
1013 if (!indentation_.empty()) *sout_ << " ";
1019 bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
1020 ArrayIndex const size = value.size();
1021 bool isMultiLine = size * 3 >= rightMargin_;
1022 childValues_.clear();
1023 for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1024 Value const& childValue = value[index];
1025 isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1026 childValue.size() > 0);
1028 if (!isMultiLine) // check if line length > max line length
1030 childValues_.reserve(size);
1031 addChildValues_ = true;
1032 ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1033 for (ArrayIndex index = 0; index < size; ++index) {
1034 if (hasCommentForValue(value[index])) {
1037 writeValue(value[index]);
1038 lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1040 addChildValues_ = false;
1041 isMultiLine = isMultiLine || lineLength >= rightMargin_;
1046 void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) {
1047 if (addChildValues_)
1048 childValues_.push_back(value);
1053 void BuiltStyledStreamWriter::writeIndent() {
1054 // blep intended this to look at the so-far-written string
1055 // to determine whether we are already indented, but
1056 // with a stream we cannot do that. So we rely on some saved state.
1057 // The caller checks indented_.
1059 if (!indentation_.empty()) {
1060 // In this case, drop newlines too.
1061 *sout_ << '\n' << indentString_;
1065 void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) {
1066 if (!indented_) writeIndent();
1071 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1073 void BuiltStyledStreamWriter::unindent() {
1074 assert(indentString_.size() >= indentation_.size());
1075 indentString_.resize(indentString_.size() - indentation_.size());
1078 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1079 if (cs_ == CommentStyle::None) return;
1080 if (!root.hasComment(commentBefore))
1083 if (!indented_) writeIndent();
1084 const JSONCPP_STRING& comment = root.getComment(commentBefore);
1085 JSONCPP_STRING::const_iterator iter = comment.begin();
1086 while (iter != comment.end()) {
1088 if (*iter == '\n' &&
1089 (iter != comment.end() && *(iter + 1) == '/'))
1090 // writeIndent(); // would write extra newline
1091 *sout_ << indentString_;
1097 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
1098 if (cs_ == CommentStyle::None) return;
1099 if (root.hasComment(commentAfterOnSameLine))
1100 *sout_ << " " + root.getComment(commentAfterOnSameLine);
1102 if (root.hasComment(commentAfter)) {
1104 *sout_ << root.getComment(commentAfter);
1109 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1110 return value.hasComment(commentBefore) ||
1111 value.hasComment(commentAfterOnSameLine) ||
1112 value.hasComment(commentAfter);
1118 StreamWriter::StreamWriter()
1122 StreamWriter::~StreamWriter()
1125 StreamWriter::Factory::~Factory()
1127 StreamWriterBuilder::StreamWriterBuilder()
1129 setDefaults(&settings_);
1131 StreamWriterBuilder::~StreamWriterBuilder()
1133 StreamWriter* StreamWriterBuilder::newStreamWriter() const
1135 JSONCPP_STRING indentation = settings_["indentation"].asString();
1136 JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
1137 bool eyc = settings_["enableYAMLCompatibility"].asBool();
1138 bool dnp = settings_["dropNullPlaceholders"].asBool();
1139 bool usf = settings_["useSpecialFloats"].asBool();
1140 unsigned int pre = settings_["precision"].asUInt();
1141 CommentStyle::Enum cs = CommentStyle::All;
1142 if (cs_str == "All") {
1143 cs = CommentStyle::All;
1144 } else if (cs_str == "None") {
1145 cs = CommentStyle::None;
1147 throwRuntimeError("commentStyle must be 'All' or 'None'");
1149 JSONCPP_STRING colonSymbol = " : ";
1152 } else if (indentation.empty()) {
1155 JSONCPP_STRING nullSymbol = "null";
1159 if (pre > 17) pre = 17;
1160 JSONCPP_STRING endingLineFeedSymbol;
1161 return new BuiltStyledStreamWriter(
1163 colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
1165 static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
1167 valid_keys->clear();
1168 valid_keys->insert("indentation");
1169 valid_keys->insert("commentStyle");
1170 valid_keys->insert("enableYAMLCompatibility");
1171 valid_keys->insert("dropNullPlaceholders");
1172 valid_keys->insert("useSpecialFloats");
1173 valid_keys->insert("precision");
1175 bool StreamWriterBuilder::validate(Json::Value* invalid) const
1177 Json::Value my_invalid;
1178 if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
1179 Json::Value& inv = *invalid;
1180 std::set<JSONCPP_STRING> valid_keys;
1181 getValidWriterKeys(&valid_keys);
1182 Value::Members keys = settings_.getMemberNames();
1183 size_t n = keys.size();
1184 for (size_t i = 0; i < n; ++i) {
1185 JSONCPP_STRING const& key = keys[i];
1186 if (valid_keys.find(key) == valid_keys.end()) {
1187 inv[key] = settings_[key];
1190 return 0u == inv.size();
1192 Value& StreamWriterBuilder::operator[](JSONCPP_STRING key)
1194 return settings_[key];
1197 void StreamWriterBuilder::setDefaults(Json::Value* settings)
1199 //! [StreamWriterBuilderDefaults]
1200 (*settings)["commentStyle"] = "All";
1201 (*settings)["indentation"] = "\t";
1202 (*settings)["enableYAMLCompatibility"] = false;
1203 (*settings)["dropNullPlaceholders"] = false;
1204 (*settings)["useSpecialFloats"] = false;
1205 (*settings)["precision"] = 17;
1206 //! [StreamWriterBuilderDefaults]
1209 JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) {
1210 JSONCPP_OSTRINGSTREAM sout;
1211 StreamWriterPtr const writer(builder.newStreamWriter());
1212 writer->write(root, &sout);
1216 JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {
1217 StreamWriterBuilder builder;
1218 StreamWriterPtr const writer(builder.newStreamWriter());
1219 writer->write(root, &sout);