| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- // Copyright 2011 Baptiste Lepilleur
- // 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/writer.h>
- #include "json_tool.h"
- #endif // if !defined(JSON_IS_AMALGAMATION)
- #include <utility>
- #include <assert.h>
- #include <stdio.h>
- #include <string.h>
- #include <sstream>
- #include <iomanip>
- #include <math.h>
- #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
- #include <float.h>
- #define isfinite _finite
- #define snprintf _snprintf
- #endif
- // Solaris
- #if defined(__sun)
- # include <ieeefp.h>
- # if !defined(isfinite)
- # define isfinite finite
- # endif
- #endif
- // AIX
- #if defined(_AIX)
- # if !defined(isfinite)
- # define isfinite finite
- # endif
- #endif
- // HP-UX
- #if defined(__hpux)
- # if !defined(isfinite)
- # if defined(__ia64) && !defined(finite)
- # define isfinite(x) ((sizeof(x) == sizeof(float) ? \
- _Isfinitef(x) : _Isfinite(x)))
- # else
- # define isfinite finite
- # endif
- # endif
- #endif
- // Ancient glibc
- #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 2
- # if !defined(isfinite)
- # define isfinite __finite
- # endif
- #endif
- #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
- // Disable warning about strdup being deprecated.
- #pragma warning(disable : 4996)
- #endif
- namespace Json {
- static bool containsControlCharacter(const char* str) {
- while (*str) {
- if (isControlCharacter(*(str++)))
- return true;
- }
- return false;
- }
- std::string valueToString(LargestInt value) {
- UIntToStringBuffer buffer;
- char* current = buffer + sizeof(buffer);
- bool isNegative = value < 0;
- if (isNegative)
- value = -value;
- uintToString(LargestUInt(value), current);
- if (isNegative)
- *--current = '-';
- assert(current >= buffer);
- return current;
- }
- std::string valueToString(LargestUInt value) {
- UIntToStringBuffer buffer;
- char* current = buffer + sizeof(buffer);
- uintToString(value, current);
- assert(current >= buffer);
- return current;
- }
- #if defined(JSON_HAS_INT64)
- std::string valueToString(Int value) {
- return valueToString(LargestInt(value));
- }
- std::string valueToString(UInt value) {
- return valueToString(LargestUInt(value));
- }
- #endif // # if defined(JSON_HAS_INT64)
- std::string valueToString(double value) {
- // Allocate a buffer that is more than large enough to store the 16 digits of
- // precision requested below.
- char buffer[32];
- int len = -1;
- // Print into the buffer. We need not request the alternative representation
- // that always has a decimal point because JSON doesn't distingish the
- // concepts of reals and integers.
- #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
- // visual studio 2005 to
- // avoid warning.
- #if defined(WINCE)
- len = _snprintf(buffer, sizeof(buffer), "%.16g", value);
- #else
- len = sprintf_s(buffer, sizeof(buffer), "%.16g", value);
- #endif
- #else
- if (isfinite(value)) {
- len = snprintf(buffer, sizeof(buffer), "%.16g", value);
- } else {
- // IEEE standard states that NaN values will not compare to themselves
- if (value != value) {
- len = snprintf(buffer, sizeof(buffer), "null");
- } else if (value < 0) {
- len = snprintf(buffer, sizeof(buffer), "-1e+9999");
- } else {
- len = snprintf(buffer, sizeof(buffer), "1e+9999");
- }
- // For those, we do not need to call fixNumLoc, but it is fast.
- }
- #endif
- assert(len >= 0);
- fixNumericLocale(buffer, buffer + len);
- return buffer;
- }
- std::string valueToString(bool value) { return value ? "true" : "false"; }
- std::string valueToQuotedString(const char* value) {
- if (value == NULL)
- return "";
- // Not sure how to handle unicode...
- if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
- !containsControlCharacter(value))
- return std::string("\"") + value + "\"";
- // We have to walk value and escape any special characters.
- // Appending to std::string is not efficient, but this should be rare.
- // (Note: forward slashes are *not* rare, but I am not escaping them.)
- std::string::size_type maxsize =
- strlen(value) * 2 + 3; // allescaped+quotes+NULL
- std::string result;
- result.reserve(maxsize); // to avoid lots of mallocs
- result += "\"";
- for (const char* c = value; *c != 0; ++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 (isControlCharacter(*c)) {
- std::ostringstream oss;
- oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
- << std::setw(4) << static_cast<int>(*c);
- result += oss.str();
- } else {
- result += *c;
- }
- break;
- }
- }
- result += "\"";
- return result;
- }
- // Class Writer
- // //////////////////////////////////////////////////////////////////
- Writer::~Writer() {}
- // Class FastWriter
- // //////////////////////////////////////////////////////////////////
- FastWriter::FastWriter()
- : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
- omitEndingLineFeed_(false) {}
- void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
- void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
- void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
- std::string FastWriter::write(const Value& root) {
- document_ = "";
- 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:
- document_ += valueToQuotedString(value.asCString());
- break;
- case booleanValue:
- document_ += valueToString(value.asBool());
- break;
- case arrayValue: {
- document_ += '[';
- int size = value.size();
- for (int 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 std::string& name = *it;
- if (it != members.begin())
- document_ += ',';
- document_ += valueToQuotedString(name.c_str());
- document_ += yamlCompatiblityEnabled_ ? ": " : ":";
- writeValue(value[name]);
- }
- document_ += '}';
- } break;
- }
- }
- // Class StyledWriter
- // //////////////////////////////////////////////////////////////////
- StyledWriter::StyledWriter()
- : rightMargin_(74), indentSize_(3), addChildValues_() {}
- std::string StyledWriter::write(const Value& root) {
- document_ = "";
- addChildValues_ = false;
- indentString_ = "";
- 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:
- pushValue(valueToQuotedString(value.asCString()));
- 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 std::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 = isMultineArray(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::isMultineArray(const Value& value) {
- int size = value.size();
- bool isMultiLine = size * 3 >= rightMargin_;
- childValues_.clear();
- for (int index = 0; index < size && !isMultiLine; ++index) {
- const Value& childValue = value[index];
- isMultiLine =
- isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
- childValue.size() > 0);
- }
- if (!isMultiLine) // check if line length > max line length
- {
- childValues_.reserve(size);
- addChildValues_ = true;
- int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
- for (int index = 0; index < size; ++index) {
- writeValue(value[index]);
- lineLength += int(childValues_[index].length());
- }
- addChildValues_ = false;
- isMultiLine = isMultiLine || lineLength >= rightMargin_;
- }
- return isMultiLine;
- }
- void StyledWriter::pushValue(const std::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 std::string& value) {
- writeIndent();
- document_ += value;
- }
- void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
- void StyledWriter::unindent() {
- assert(int(indentString_.size()) >= indentSize_);
- indentString_.resize(indentString_.size() - indentSize_);
- }
- void StyledWriter::writeCommentBeforeValue(const Value& root) {
- if (!root.hasComment(commentBefore))
- return;
- document_ += "\n";
- writeIndent();
- std::string normalizedComment = normalizeEOL(root.getComment(commentBefore));
- std::string::const_iterator iter = normalizedComment.begin();
- while (iter != normalizedComment.end()) {
- document_ += *iter;
- if (*iter == '\n' && *(iter + 1) == '/')
- writeIndent();
- ++iter;
- }
- // Comments are stripped of newlines, so add one here
- document_ += "\n";
- }
- void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
- if (root.hasComment(commentAfterOnSameLine))
- document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
- if (root.hasComment(commentAfter)) {
- document_ += "\n";
- document_ += normalizeEOL(root.getComment(commentAfter));
- document_ += "\n";
- }
- }
- bool StyledWriter::hasCommentForValue(const Value& value) {
- return value.hasComment(commentBefore) ||
- value.hasComment(commentAfterOnSameLine) ||
- value.hasComment(commentAfter);
- }
- std::string StyledWriter::normalizeEOL(const std::string& text) {
- std::string normalized;
- normalized.reserve(text.length());
- const char* begin = text.c_str();
- const char* end = begin + text.length();
- const char* current = begin;
- while (current != end) {
- char c = *current++;
- if (c == '\r') // mac or dos EOL
- {
- if (*current == '\n') // convert dos EOL
- ++current;
- normalized += '\n';
- } else // handle unix EOL & other char
- normalized += c;
- }
- return normalized;
- }
- // Class StyledStreamWriter
- // //////////////////////////////////////////////////////////////////
- StyledStreamWriter::StyledStreamWriter(std::string indentation)
- : document_(NULL), rightMargin_(74), indentation_(indentation),
- addChildValues_() {}
- void StyledStreamWriter::write(std::ostream& out, const Value& root) {
- document_ = &out;
- addChildValues_ = false;
- indentString_ = "";
- writeCommentBeforeValue(root);
- 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:
- pushValue(valueToQuotedString(value.asCString()));
- 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 std::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 = isMultineArray(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 StyledStreamWriter::isMultineArray(const Value& value) {
- int size = value.size();
- bool isMultiLine = size * 3 >= rightMargin_;
- childValues_.clear();
- for (int index = 0; index < size && !isMultiLine; ++index) {
- const Value& childValue = value[index];
- isMultiLine =
- isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
- childValue.size() > 0);
- }
- if (!isMultiLine) // check if line length > max line length
- {
- childValues_.reserve(size);
- addChildValues_ = true;
- int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
- for (int index = 0; index < size; ++index) {
- writeValue(value[index]);
- lineLength += int(childValues_[index].length());
- }
- addChildValues_ = false;
- isMultiLine = isMultiLine || lineLength >= rightMargin_;
- }
- return isMultiLine;
- }
- void StyledStreamWriter::pushValue(const std::string& value) {
- if (addChildValues_)
- childValues_.push_back(value);
- else
- *document_ << value;
- }
- void StyledStreamWriter::writeIndent() {
- /*
- Some comments in this method would have been nice. ;-)
- 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_ << '\n' << indentString_;
- }
- void StyledStreamWriter::writeWithIndent(const std::string& value) {
- writeIndent();
- *document_ << value;
- }
- 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;
- *document_ << normalizeEOL(root.getComment(commentBefore));
- *document_ << "\n";
- }
- void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
- if (root.hasComment(commentAfterOnSameLine))
- *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
- if (root.hasComment(commentAfter)) {
- *document_ << "\n";
- *document_ << normalizeEOL(root.getComment(commentAfter));
- *document_ << "\n";
- }
- }
- bool StyledStreamWriter::hasCommentForValue(const Value& value) {
- return value.hasComment(commentBefore) ||
- value.hasComment(commentAfterOnSameLine) ||
- value.hasComment(commentAfter);
- }
- std::string StyledStreamWriter::normalizeEOL(const std::string& text) {
- std::string normalized;
- normalized.reserve(text.length());
- const char* begin = text.c_str();
- const char* end = begin + text.length();
- const char* current = begin;
- while (current != end) {
- char c = *current++;
- if (c == '\r') // mac or dos EOL
- {
- if (*current == '\n') // convert dos EOL
- ++current;
- normalized += '\n';
- } else // handle unix EOL & other char
- normalized += c;
- }
- return normalized;
- }
- std::ostream& operator<<(std::ostream& sout, const Value& root) {
- Json::StyledStreamWriter writer;
- writer.write(sout, root);
- return sout;
- }
- } // namespace Json
|