// 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 "json_tool.h"#include <json/writer.h>#endif // if !defined(JSON_IS_AMALGAMATION)#include <algorithm>#include <cassert>#include <cctype>#include <cstring>#include <iomanip>#include <memory>#include <set>#include <sstream>#include <utility>#if __cplusplus >= 201103L#include <cmath>#include <cstdio>#if !defined(isnan)#define isnan std::isnan#endif#if !defined(isfinite)#define isfinite std::isfinite#endif#else#include <cmath>#include <cstdio>#if defined(_MSC_VER)#if !defined(isnan)#include <float.h>#define isnan _isnan#endif#if !defined(isfinite)#include <float.h>#define isfinite _finite#endif#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES#endif //_MSC_VER#if defined(__sun) && defined(__SVR4) // Solaris#if !defined(isfinite)#include <ieeefp.h>#define isfinite finite#endif#endif#if defined(__hpux)#if !defined(isfinite)#if defined(__ia64) && !defined(finite)#define isfinite(x) \((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))#endif#endif#endif#if !defined(isnan)// IEEE standard states that NaN values will not compare to themselves#define isnan(x) ((x) != (x))#endif#if !defined(__APPLE__)#if !defined(isfinite)#define isfinite finite#endif#endif#endif#if defined(_MSC_VER)// Disable warning about strdup being deprecated.#pragma warning(disable : 4996)#endifnamespace Json {#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)using StreamWriterPtr = std::unique_ptr<StreamWriter>;#elseusing StreamWriterPtr = std::auto_ptr<StreamWriter>;#endifString 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;}String valueToString(LargestUInt value) {UIntToStringBuffer buffer;char* current = buffer + sizeof(buffer);uintToString(value, current);assert(current >= buffer);return current;}#if defined(JSON_HAS_INT64)String valueToString(Int value) { return valueToString(LargestInt(value)); }String valueToString(UInt value) { return valueToString(LargestUInt(value)); }#endif // # if defined(JSON_HAS_INT64)namespace {String valueToString(double value, bool useSpecialFloats,unsigned int precision, PrecisionType precisionType) {// 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)) {static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},{"null", "-1e+9999", "1e+9999"}};return reps[useSpecialFloats ? 0 : 1][isnan(value) ? 0 : (value < 0) ? 1 : 2];}String buffer(size_t(36), '0円');while (true) {int len = jsoncpp_snprintf(&*buffer.begin(), buffer.size(),(precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",precision, value);assert(len >= 0);auto wouldPrint = static_cast<size_t>(len);if (wouldPrint >= buffer.size()) {buffer.resize(wouldPrint + 1);continue;}buffer.resize(wouldPrint);break;}buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());// try to ensure we preserve the fact that this was given to us as a double on// inputif (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {buffer += ".0";}// strip the zero padding from the rightif (precisionType == PrecisionType::decimalPlaces) {buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),buffer.end());}return buffer;}} // namespaceString valueToString(double value, unsigned int precision,PrecisionType precisionType) {return valueToString(value, false, precision, precisionType);}String valueToString(bool value) { return value ? "true" : "false"; }static bool doesAnyCharRequireEscaping(char const* s, size_t n) {assert(s || !n);return std::any_of(s, s + n, [](unsigned char c) {return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;});}static unsigned int utf8ToCodepoint(const char*& s, const char* e) {const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;unsigned int firstByte = static_cast<unsigned char>(*s);if (firstByte < 0x80)return firstByte;if (firstByte < 0xE0) {if (e - s < 2)return REPLACEMENT_CHARACTER;unsigned int calculated =((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);s += 1;// oversized encoded characters are invalidreturn calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;}if (firstByte < 0xF0) {if (e - s < 3)return REPLACEMENT_CHARACTER;unsigned int calculated = ((firstByte & 0x0F) << 12) |((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |(static_cast<unsigned int>(s[2]) & 0x3F);s += 2;// surrogates aren't valid codepoints itself// shouldn't be UTF-8 encodedif (calculated >= 0xD800 && calculated <= 0xDFFF)return REPLACEMENT_CHARACTER;// oversized encoded characters are invalidreturn calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;}if (firstByte < 0xF8) {if (e - s < 4)return REPLACEMENT_CHARACTER;unsigned int calculated = ((firstByte & 0x07) << 18) |((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |(static_cast<unsigned int>(s[3]) & 0x3F);s += 3;// oversized encoded characters are invalidreturn 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 String toHex16Bit(unsigned int x) {const unsigned int hi = (x >> 8) & 0xff;const unsigned int lo = x & 0xff;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 void appendRaw(String& result, unsigned ch) {result += static_cast<char>(ch);}static void appendHex(String& result, unsigned ch) {result.append("\\u").append(toHex16Bit(ch));}static String valueToQuotedStringN(const char* value, size_t length,bool emitUTF8 = false) {if (value == nullptr)return "";if (!doesAnyCharRequireEscaping(value, length))return String("\"") + value + "\"";// We have to walk value and escape any special characters.// Appending to String is not efficient, but this should be rare.// (Note: forward slashes are *not* rare, but I am not escaping them.)String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULLString result;result.reserve(maxsize); // to avoid lots of mallocsresult += "\"";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 </// sequence.// Should add a flag to allow this compatibility mode and prevent this// sequence from occurring.default: {if (emitUTF8) {unsigned codepoint = static_cast<unsigned char>(*c);if (codepoint < 0x20) {appendHex(result, codepoint);} else {appendRaw(result, codepoint);}} else {unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`if (codepoint < 0x20) {appendHex(result, codepoint);} else if (codepoint < 0x80) {appendRaw(result, codepoint);} else if (codepoint < 0x10000) {// Basic Multilingual PlaneappendHex(result, codepoint);} else {// Extended Unicode. Encode 20 bits as a surrogate pair.codepoint -= 0x10000;appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));appendHex(result, 0xdc00 + (codepoint & 0x3ff));}}} break;}}result += "\"";return result;}String valueToQuotedString(const char* value) {return valueToQuotedStringN(value, strlen(value));}// Class Writer// //////////////////////////////////////////////////////////////////Writer::~Writer() = default;// Class FastWriter// //////////////////////////////////////////////////////////////////FastWriter::FastWriter()= default;void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }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<size_t>(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 (auto it = members.begin(); it != members.end(); ++it) {const String& name = *it;if (it != members.begin())document_ += ',';document_ += valueToQuotedStringN(name.data(), name.length());document_ += yamlCompatibilityEnabled_ ? ": " : ":";writeValue(value[name]);}document_ += '}';} break;}}// Class StyledWriter// //////////////////////////////////////////////////////////////////StyledWriter::StyledWriter() = default;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<size_t>(end - str)));elsepushValue("");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();auto it = members.begin();for (;;) {const 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) {size_t size = value.size();if (size == 0)pushValue("[]");else {bool isArrayMultiLine = isMultilineArray(value);if (isArrayMultiLine) {writeWithIndent("[");indent();bool hasChildValue = !childValues_.empty();ArrayIndex 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 (size_t 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.empty());}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<ArrayIndex>(childValues_[index].length());}addChildValues_ = false;isMultiLine = isMultiLine || lineLength >= rightMargin_;}return isMultiLine;}void StyledWriter::pushValue(const String& value) {if (addChildValues_)childValues_.push_back(value);elsedocument_ += value;}void StyledWriter::writeIndent() {if (!document_.empty()) {char last = document_[document_.length() - 1];if (last == ' ') // already indentedreturn;if (last != '\n') // Comments may add new-linedocument_ += '\n';}document_ += indentString_;}void StyledWriter::writeWithIndent(const String& value) {writeIndent();document_ += value;}void StyledWriter::indent() { indentString_ += 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 String& comment = root.getComment(commentBefore);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 heredocument_ += '\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(String indentation): document_(nullptr), indentation_(std::move(indentation)),addChildValues_(), indented_(false) {}void StyledStreamWriter::write(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_ = nullptr; // 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<size_t>(end - str)));elsepushValue("");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();auto it = members.begin();for (;;) {const 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.empty());}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<ArrayIndex>(childValues_[index].length());}addChildValues_ = false;isMultiLine = isMultiLine || lineLength >= rightMargin_;}return isMultiLine;}void StyledStreamWriter::pushValue(const 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 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 String& comment = root.getComment(commentBefore);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(String indentation, CommentStyle::Enum cs,String colonSymbol, String nullSymbol,String endingLineFeedSymbol, bool useSpecialFloats,bool emitUTF8, unsigned int precision,PrecisionType precisionType);int write(Value const& root, OStream* sout) override;private:void writeValue(Value const& value);void writeArrayValue(Value const& value);bool isMultilineArray(Value const& value);void pushValue(String const& value);void writeIndent();void writeWithIndent(String const& value);void indent();void unindent();void writeCommentBeforeValue(Value const& root);void writeCommentAfterValueOnSameLine(Value const& root);static bool hasCommentForValue(const Value& value);using ChildValues = std::vector<String>;ChildValues childValues_;String indentString_;unsigned int rightMargin_;String indentation_;CommentStyle::Enum cs_;String colonSymbol_;String nullSymbol_;String endingLineFeedSymbol_;bool addChildValues_ : 1;bool indented_ : 1;bool useSpecialFloats_ : 1;bool emitUTF8_ : 1;unsigned int precision_;PrecisionType precisionType_;};BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs, String colonSymbol,String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,bool emitUTF8, unsigned int precision, PrecisionType precisionType): rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),addChildValues_(false), indented_(false),useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),precision_(precision), precisionType_(precisionType) {}int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {sout_ = sout;addChildValues_ = false;indented_ = true;indentString_.clear();writeCommentBeforeValue(root);if (!indented_)writeIndent();indented_ = true;writeValue(root);writeCommentAfterValueOnSameLine(root);*sout_ << endingLineFeedSymbol_;sout_ = nullptr;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_,precisionType_));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<size_t>(end - str), emitUTF8_));elsepushValue("");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();auto it = members.begin();for (;;) {String const& name = *it;Value const& childValue = value[name];writeCommentBeforeValue(childValue);writeWithIndent(valueToQuotedStringN(name.data(), name.length(), emitUTF8_));*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.empty());}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<ArrayIndex>(childValues_[index].length());}addChildValues_ = false;isMultiLine = isMultiLine || lineLength >= rightMargin_;}return isMultiLine;}void BuiltStyledStreamWriter::pushValue(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(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 String& comment = root.getComment(commentBefore);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);}}// staticbool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {return value.hasComment(commentBefore) ||value.hasComment(commentAfterOnSameLine) ||value.hasComment(commentAfter);}///////////////// StreamWriterStreamWriter::StreamWriter() : sout_(nullptr) {}StreamWriter::~StreamWriter() = default;StreamWriter::Factory::~Factory() = default;StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }StreamWriterBuilder::~StreamWriterBuilder() = default;StreamWriter* StreamWriterBuilder::newStreamWriter() const {const String indentation = settings_["indentation"].asString();const String cs_str = settings_["commentStyle"].asString();const String pt_str = settings_["precisionType"].asString();const bool eyc = settings_["enableYAMLCompatibility"].asBool();const bool dnp = settings_["dropNullPlaceholders"].asBool();const bool usf = settings_["useSpecialFloats"].asBool();const bool emitUTF8 = settings_["emitUTF8"].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'");}PrecisionType precisionType(significantDigits);if (pt_str == "significant") {precisionType = PrecisionType::significantDigits;} else if (pt_str == "decimal") {precisionType = PrecisionType::decimalPlaces;} else {throwRuntimeError("precisionType must be 'significant' or 'decimal'");}String colonSymbol = " : ";if (eyc) {colonSymbol = ": ";} else if (indentation.empty()) {colonSymbol = ":";}String nullSymbol = "null";if (dnp) {nullSymbol.clear();}if (pre > 17)pre = 17;String endingLineFeedSymbol;return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,endingLineFeedSymbol, usf, emitUTF8, pre,precisionType);}bool StreamWriterBuilder::validate(Json::Value* invalid) const {static const auto& valid_keys = *new std::set<String>{"indentation","commentStyle","enableYAMLCompatibility","dropNullPlaceholders","useSpecialFloats","emitUTF8","precision","precisionType",};for (auto si = settings_.begin(); si != settings_.end(); ++si) {auto key = si.name();if (valid_keys.count(key))continue;if (invalid)(*invalid)[key] = *si;elsereturn false;}return invalid ? invalid->empty() : true;}Value& StreamWriterBuilder::operator[](const String& key) {return settings_[key];}// staticvoid StreamWriterBuilder::setDefaults(Json::Value* settings) {//! [StreamWriterBuilderDefaults](*settings)["commentStyle"] = "All";(*settings)["indentation"] = "\t";(*settings)["enableYAMLCompatibility"] = false;(*settings)["dropNullPlaceholders"] = false;(*settings)["useSpecialFloats"] = false;(*settings)["emitUTF8"] = false;(*settings)["precision"] = 17;(*settings)["precisionType"] = "significant";//! [StreamWriterBuilderDefaults]}String writeString(StreamWriter::Factory const& factory, Value const& root) {OStringStream sout;StreamWriterPtr const writer(factory.newStreamWriter());writer->write(root, &sout);return sout.str();}OStream& operator<<(OStream& sout, Value const& root) {StreamWriterBuilder builder;StreamWriterPtr const writer(builder.newStreamWriter());writer->write(root, &sout);return sout;}} // namespace Json
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。