// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) #include #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 #include #define isfinite _finite #elif defined(__sun) && defined(__SVR4) //Solaris #if !defined(isfinite) #include #define isfinite finite #endif #elif defined(_AIX) #if !defined(isfinite) #include #define isfinite finite #endif #elif defined(__hpux) #if !defined(isfinite) #if defined(__ia64) && !defined(finite) #define isfinite(x) ((sizeof(x) == sizeof(float) ? \ _Isfinitef(x) : _IsFinite(x))) #else #include #define isfinite finite #endif #endif #else #include #if !(defined(__QNXNTO__)) // QNX already defines isfinite #define isfinite std::isfinite #endif #endif #if defined(_MSC_VER) #if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above #define snprintf sprintf_s #elif _MSC_VER >= 1900 // VC++ 14.0 and above #define snprintf std::snprintf #else #define snprintf _snprintf #endif #elif defined(__ANDROID__) || defined(__QNXNTO__) #define snprintf snprintf #elif __cplusplus >= 201103L #if !defined(__MINGW32__) && !defined(__CYGWIN__) #define snprintf std::snprintf #endif #endif #if defined(__BORLANDC__) #include #define isfinite _finite #define snprintf _snprintf #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) typedef std::unique_ptr StreamWriterPtr; #else typedef std::auto_ptr StreamWriterPtr; #endif JSONCPP_STRING valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); if (value == Value::minLargestInt) { uintToString(LargestUInt(Value::maxLargestInt) + 1, current); *--current = '-'; } else if (value < 0) { uintToString(LargestUInt(-value), current); *--current = '-'; } else { uintToString(LargestUInt(value), current); } assert(current >= buffer); return current; } JSONCPP_STRING valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); assert(current >= buffer); return current; } #if defined(JSON_HAS_INT64) JSONCPP_STRING valueToString(Int value) { return valueToString(LargestInt(value)); } JSONCPP_STRING valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) namespace { JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { // Allocate a buffer that is more than large enough to store the 16 digits of // precision requested below. char buffer[36]; int len = -1; char formatString[15]; snprintf(formatString, sizeof(formatString), "%%.%ug", precision); // Print into the buffer. We need not request the alternative representation // that always has a decimal point because JSON doesn't distinguish the // concepts of reals and integers. if (isfinite(value)) { len = snprintf(buffer, sizeof(buffer), formatString, value); fixNumericLocale(buffer, buffer + len); // try to ensure we preserve the fact that this was given to us as a double on input if (!strchr(buffer, '.') && !strchr(buffer, 'e')) { strcat(buffer, ".0"); } } else { // IEEE standard states that NaN values will not compare to themselves if (value != value) { len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); } else if (value < 0) { len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); } else { len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); } } assert(len >= 0); return buffer; } } JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } static bool isAnyCharRequiredQuoting(char const* s, size_t n) { assert(s || !n); char const* const end = s + n; for (char const* cur = s; cur < end; ++cur) { if (*cur == '\\' || *cur == '\"' || *cur < ' ' || static_cast(*cur) < 0x80) return true; } return false; } static unsigned int utf8ToCodepoint(const char*& s, const char* e) { const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; unsigned int firstByte = static_cast(*s); if (firstByte < 0x80) return firstByte; if (firstByte < 0xE0) { if (e - s < 2) return REPLACEMENT_CHARACTER; unsigned int calculated = ((firstByte & 0x1F) << 6) | (static_cast(s[1]) & 0x3F); s += 1; // oversized encoded characters are invalid return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; } if (firstByte < 0xF0) { if (e - s < 3) return REPLACEMENT_CHARACTER; unsigned int calculated = ((firstByte & 0x0F) << 12) | ((static_cast(s[1]) & 0x3F) << 6) | (static_cast(s[2]) & 0x3F); s += 2; // surrogates aren't valid codepoints itself // shouldn't be UTF-8 encoded if (calculated >= 0xD800 && calculated <= 0xDFFF) return REPLACEMENT_CHARACTER; // oversized encoded characters are invalid return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; } if (firstByte < 0xF8) { if (e - s < 4) return REPLACEMENT_CHARACTER; unsigned int calculated = ((firstByte & 0x07) << 24) | ((static_cast(s[1]) & 0x3F) << 12) | ((static_cast(s[2]) & 0x3F) << 6) | (static_cast(s[3]) & 0x3F); s += 3; // oversized encoded characters are invalid return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; } return REPLACEMENT_CHARACTER; } static const char hex2[] = "000102030405060708090a0b0c0d0e0f" "101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f" "303132333435363738393a3b3c3d3e3f" "404142434445464748494a4b4c4d4e4f" "505152535455565758595a5b5c5d5e5f" "606162636465666768696a6b6c6d6e6f" "707172737475767778797a7b7c7d7e7f" "808182838485868788898a8b8c8d8e8f" "909192939495969798999a9b9c9d9e9f" "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; static JSONCPP_STRING toHex16Bit(unsigned int x) { const unsigned int hi = (x >> 8) & 0xff; const unsigned int lo = x & 0xff; JSONCPP_STRING result(4, ' '); result[0] = hex2[2 * hi]; result[1] = hex2[2 * hi + 1]; result[2] = hex2[2 * lo]; result[3] = hex2[2 * lo + 1]; return result; } static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { if (value == NULL) return ""; if (!isAnyCharRequiredQuoting(value, length)) return JSONCPP_STRING("\"") + value + "\""; // We have to walk value and escape any special characters. // Appending to JSONCPP_STRING is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) JSONCPP_STRING::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL JSONCPP_STRING result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; char const* end = value + length; for (const char* c = value; c != end; ++c) { switch (*c) { case '\"': result += "\\\""; break; case '\\': result += "\\\\"; break; case '\b': result += "\\b"; break; case '\f': result += "\\f"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; // case '/': // Even though \/ is considered a legal escape in JSON, a bare // slash is also legal, so I see no reason to escape it. // (I hope I am not misunderstanding something.) // blep notes: actually escaping \/ may be useful in javascript to avoid = 0x20) result += static_cast(cp); else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane result += "\\u"; result += toHex16Bit(cp); } else { // codepoint is not in Basic Multilingual Plane // convert to surrogate pair first cp -= 0x10000; result += "\\u"; result += toHex16Bit((cp >> 10) + 0xD800); result += "\\u"; result += toHex16Bit((cp & 0x3FF) + 0xDC00); } } break; } } result += "\""; return result; } JSONCPP_STRING valueToQuotedString(const char* value) { return valueToQuotedStringN(value, static_cast(strlen(value))); } // Class Writer // ////////////////////////////////////////////////////////////////// Writer::~Writer() {} // Class FastWriter // ////////////////////////////////////////////////////////////////// FastWriter::FastWriter() : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false), omitEndingLineFeed_(false) {} void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } JSONCPP_STRING FastWriter::write(const Value& root) { document_.clear(); writeValue(root); if (!omitEndingLineFeed_) document_ += "\n"; return document_; } void FastWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: if (!dropNullPlaceholders_) document_ += "null"; break; case intValue: document_ += valueToString(value.asLargestInt()); break; case uintValue: document_ += valueToString(value.asLargestUInt()); break; case realValue: document_ += valueToString(value.asDouble()); break; case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); break; } case booleanValue: document_ += valueToString(value.asBool()); break; case arrayValue: { document_ += '['; ArrayIndex size = value.size(); for (ArrayIndex index = 0; index < size; ++index) { if (index > 0) document_ += ','; writeValue(value[index]); } document_ += ']'; } break; case objectValue: { Value::Members members(value.getMemberNames()); document_ += '{'; for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) { const JSONCPP_STRING& name = *it; if (it != members.begin()) document_ += ','; document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); document_ += yamlCompatibilityEnabled_ ? ": " : ":"; writeValue(value[name]); } document_ += '}'; } break; } } // Class StyledWriter // ////////////////////////////////////////////////////////////////// StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} JSONCPP_STRING StyledWriter::write(const Value& root) { document_.clear(); addChildValues_ = false; indentString_.clear(); writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); document_ += "\n"; return document_; } void StyledWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: pushValue("null"); break; case intValue: pushValue(valueToString(value.asLargestInt())); break; case uintValue: pushValue(valueToString(value.asLargestUInt())); break; case realValue: pushValue(valueToString(value.asDouble())); break; case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: pushValue(valueToString(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); Value::Members::iterator it = members.begin(); for (;;) { const JSONCPP_STRING& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); document_ += " : "; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } document_ += ','; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("}"); } } break; } } void StyledWriter::writeArrayValue(const Value& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); else { bool isArrayMultiLine = isMultilineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { const Value& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); else { writeIndent(); writeValue(childValue); } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); break; } document_ += ','; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("]"); } else // output on a single line { assert(childValues_.size() == size); document_ += "[ "; for (unsigned index = 0; index < size; ++index) { if (index > 0) document_ += ", "; document_ += childValues_[index]; } document_ += " ]"; } } } bool StyledWriter::isMultilineArray(const Value& value) { ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (ArrayIndex index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); lineLength += static_cast(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void StyledWriter::pushValue(const JSONCPP_STRING& value) { if (addChildValues_) childValues_.push_back(value); else document_ += value; } void StyledWriter::writeIndent() { if (!document_.empty()) { char last = document_[document_.length() - 1]; if (last == ' ') // already indented return; if (last != '\n') // Comments may add new-line document_ += '\n'; } document_ += indentString_; } void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { writeIndent(); document_ += value; } void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } void StyledWriter::unindent() { assert(indentString_.size() >= indentSize_); indentString_.resize(indentString_.size() - indentSize_); } void StyledWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; document_ += "\n"; writeIndent(); const JSONCPP_STRING& comment = root.getComment(commentBefore); JSONCPP_STRING::const_iterator iter = comment.begin(); while (iter != comment.end()) { document_ += *iter; if (*iter == '\n' && ((iter+1) != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } // Comments are stripped of trailing newlines, so add one here document_ += "\n"; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { document_ += "\n"; document_ += root.getComment(commentAfter); document_ += "\n"; } } bool StyledWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {} void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { document_ = &out; addChildValues_ = false; indentString_.clear(); indented_ = true; writeCommentBeforeValue(root); if (!indented_) writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; document_ = NULL; // Forget the stream, for safety. } void StyledStreamWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: pushValue("null"); break; case intValue: pushValue(valueToString(value.asLargestInt())); break; case uintValue: pushValue(valueToString(value.asLargestUInt())); break; case realValue: pushValue(valueToString(value.asDouble())); break; case stringValue: { // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: pushValue(valueToString(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); Value::Members::iterator it = members.begin(); for (;;) { const JSONCPP_STRING& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); *document_ << " : "; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } *document_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("}"); } } break; } } void StyledStreamWriter::writeArrayValue(const Value& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); else { bool isArrayMultiLine = isMultilineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { const Value& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); else { if (!indented_) writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); break; } *document_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("]"); } else // output on a single line { assert(childValues_.size() == size); *document_ << "[ "; for (unsigned index = 0; index < size; ++index) { if (index > 0) *document_ << ", "; *document_ << childValues_[index]; } *document_ << " ]"; } } } bool StyledStreamWriter::isMultilineArray(const Value& value) { ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (ArrayIndex index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); lineLength += static_cast(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { if (addChildValues_) childValues_.push_back(value); else *document_ << value; } void StyledStreamWriter::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. // The caller checks indented_. *document_ << '\n' << indentString_; } void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { if (!indented_) writeIndent(); *document_ << value; indented_ = false; } void StyledStreamWriter::indent() { indentString_ += indentation_; } void StyledStreamWriter::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; if (!indented_) writeIndent(); const JSONCPP_STRING& comment = root.getComment(commentBefore); JSONCPP_STRING::const_iterator iter = comment.begin(); while (iter != comment.end()) { *document_ << *iter; if (*iter == '\n' && ((iter+1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would include newline *document_ << indentString_; ++iter; } indented_ = false; } void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) *document_ << ' ' << root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { writeIndent(); *document_ << root.getComment(commentAfter); } indented_ = false; } bool StyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } ////////////////////////// // BuiltStyledStreamWriter /// Scoped enums are not available until C++11. struct CommentStyle { /// Decide whether to write comments. enum Enum { None, ///< Drop all comments. Most, ///< Recover odd behavior of previous versions (not implemented yet). All ///< Keep all comments. }; }; struct BuiltStyledStreamWriter : public StreamWriter { BuiltStyledStreamWriter( JSONCPP_STRING const& indentation, CommentStyle::Enum cs, JSONCPP_STRING const& colonSymbol, JSONCPP_STRING const& nullSymbol, JSONCPP_STRING const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision); int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; private: void writeValue(Value const& value); void writeArrayValue(Value const& value); bool isMultilineArray(Value const& value); void pushValue(JSONCPP_STRING const& value); void writeIndent(); void writeWithIndent(JSONCPP_STRING const& value); void indent(); void unindent(); void writeCommentBeforeValue(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root); static bool hasCommentForValue(const Value& value); typedef std::vector ChildValues; ChildValues childValues_; JSONCPP_STRING indentString_; unsigned int rightMargin_; JSONCPP_STRING indentation_; CommentStyle::Enum cs_; JSONCPP_STRING colonSymbol_; JSONCPP_STRING nullSymbol_; JSONCPP_STRING endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; bool useSpecialFloats_ : 1; unsigned int precision_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( JSONCPP_STRING const& indentation, CommentStyle::Enum cs, JSONCPP_STRING const& colonSymbol, JSONCPP_STRING const& nullSymbol, JSONCPP_STRING const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision) : rightMargin_(74) , indentation_(indentation) , cs_(cs) , colonSymbol_(colonSymbol) , nullSymbol_(nullSymbol) , endingLineFeedSymbol_(endingLineFeedSymbol) , addChildValues_(false) , indented_(false) , useSpecialFloats_(useSpecialFloats) , precision_(precision) { } int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) { sout_ = sout; addChildValues_ = false; indented_ = true; indentString_.clear(); writeCommentBeforeValue(root); if (!indented_) writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *sout_ << endingLineFeedSymbol_; sout_ = NULL; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { switch (value.type()) { case nullValue: pushValue(nullSymbol_); break; case intValue: pushValue(valueToString(value.asLargestInt())); break; case uintValue: pushValue(valueToString(value.asLargestUInt())); break; case realValue: pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); break; case stringValue: { // Is NULL is possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: pushValue(valueToString(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); Value::Members::iterator it = members.begin(); for (;;) { JSONCPP_STRING const& name = *it; Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); *sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } *sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("}"); } } break; } } void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); else { bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); if (isMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { Value const& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); else { if (!indented_) writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); break; } *sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("]"); } else // output on a single line { assert(childValues_.size() == size); *sout_ << "["; if (!indentation_.empty()) *sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) *sout_ << ((!indentation_.empty()) ? ", " : ","); *sout_ << childValues_[index]; } if (!indentation_.empty()) *sout_ << " "; *sout_ << "]"; } } } bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (ArrayIndex index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); lineLength += static_cast(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { if (addChildValues_) childValues_.push_back(value); else *sout_ << value; } void BuiltStyledStreamWriter::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. // The caller checks indented_. if (!indentation_.empty()) { // In this case, drop newlines too. *sout_ << '\n' << indentString_; } } void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { if (!indented_) writeIndent(); *sout_ << value; indented_ = false; } void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } void BuiltStyledStreamWriter::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { if (cs_ == CommentStyle::None) return; if (!root.hasComment(commentBefore)) return; if (!indented_) writeIndent(); const JSONCPP_STRING& comment = root.getComment(commentBefore); JSONCPP_STRING::const_iterator iter = comment.begin(); while (iter != comment.end()) { *sout_ << *iter; if (*iter == '\n' && ((iter+1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline *sout_ << indentString_; ++iter; } indented_ = false; } void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { if (cs_ == CommentStyle::None) return; if (root.hasComment(commentAfterOnSameLine)) *sout_ << " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { writeIndent(); *sout_ << root.getComment(commentAfter); } } // static bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } /////////////// // StreamWriter StreamWriter::StreamWriter() : sout_(NULL) { } StreamWriter::~StreamWriter() { } StreamWriter::Factory::~Factory() {} StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } StreamWriterBuilder::~StreamWriterBuilder() {} StreamWriter* StreamWriterBuilder::newStreamWriter() const { JSONCPP_STRING indentation = settings_["indentation"].asString(); JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); bool eyc = settings_["enableYAMLCompatibility"].asBool(); bool dnp = settings_["dropNullPlaceholders"].asBool(); bool usf = settings_["useSpecialFloats"].asBool(); unsigned int pre = settings_["precision"].asUInt(); CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { cs = CommentStyle::All; } else if (cs_str == "None") { cs = CommentStyle::None; } else { throwRuntimeError("commentStyle must be 'All' or 'None'"); } JSONCPP_STRING colonSymbol = " : "; if (eyc) { colonSymbol = ": "; } else if (indentation.empty()) { colonSymbol = ":"; } JSONCPP_STRING nullSymbol = "null"; if (dnp) { nullSymbol.clear(); } if (pre > 17) pre = 17; JSONCPP_STRING endingLineFeedSymbol; return new BuiltStyledStreamWriter( indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); } static void getValidWriterKeys(std::set* valid_keys) { valid_keys->clear(); valid_keys->insert("indentation"); valid_keys->insert("commentStyle"); valid_keys->insert("enableYAMLCompatibility"); valid_keys->insert("dropNullPlaceholders"); valid_keys->insert("useSpecialFloats"); valid_keys->insert("precision"); } bool StreamWriterBuilder::validate(Json::Value* invalid) const { Json::Value my_invalid; if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL Json::Value& inv = *invalid; std::set valid_keys; getValidWriterKeys(&valid_keys); Value::Members keys = settings_.getMemberNames(); size_t n = keys.size(); for (size_t i = 0; i < n; ++i) { JSONCPP_STRING const& key = keys[i]; if (valid_keys.find(key) == valid_keys.end()) { inv[key] = settings_[key]; } } return 0u == inv.size(); } Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) { return settings_[key]; } // static void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; (*settings)["indentation"] = "\t"; (*settings)["enableYAMLCompatibility"] = false; (*settings)["dropNullPlaceholders"] = false; (*settings)["useSpecialFloats"] = false; (*settings)["precision"] = 17; //! [StreamWriterBuilderDefaults] } JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { JSONCPP_OSTRINGSTREAM sout; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout.str(); } JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { StreamWriterBuilder builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout; } } // namespace Json