2018-01-26 17:06:56 +01:00
|
|
|
// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
|
2015-04-27 22:25:09 +02:00
|
|
|
// 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 <iomanip>
|
2018-01-26 17:06:56 +01:00
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
|
|
|
#include <utility>
|
|
|
|
#include <set>
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstring>
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
|
|
|
|
#include <float.h>
|
|
|
|
#define isfinite _finite
|
|
|
|
#elif defined(__sun) && defined(__SVR4) //Solaris
|
|
|
|
#if !defined(isfinite)
|
|
|
|
#include <ieeefp.h>
|
|
|
|
#define isfinite finite
|
|
|
|
#endif
|
|
|
|
#elif defined(_AIX)
|
|
|
|
#if !defined(isfinite)
|
2015-04-27 22:25:09 +02:00
|
|
|
#include <math.h>
|
2018-01-26 17:06:56 +01:00
|
|
|
#define isfinite finite
|
|
|
|
#endif
|
|
|
|
#elif defined(__hpux)
|
2020-02-01 23:06:01 +01:00
|
|
|
#if !defined(isfinite) && !defined(__GNUC__)
|
2018-01-26 17:06:56 +01:00
|
|
|
#if defined(__ia64) && !defined(finite)
|
|
|
|
#define isfinite(x) ((sizeof(x) == sizeof(float) ? \
|
|
|
|
_Isfinitef(x) : _IsFinite(x)))
|
|
|
|
#else
|
|
|
|
#include <math.h>
|
|
|
|
#define isfinite finite
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#include <cmath>
|
|
|
|
#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
|
2015-04-27 22:25:09 +02:00
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
#if defined(__BORLANDC__)
|
2015-04-27 22:25:09 +02:00
|
|
|
#include <float.h>
|
|
|
|
#define isfinite _finite
|
|
|
|
#define snprintf _snprintf
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Solaris
|
|
|
|
#if defined(__sun)
|
|
|
|
# include <ieeefp.h>
|
2015-11-17 17:22:37 +01:00
|
|
|
# if !defined(isfinite)
|
|
|
|
# define isfinite finite
|
|
|
|
# endif
|
2015-04-27 22:25:09 +02:00
|
|
|
#endif
|
|
|
|
|
2015-08-17 11:37:30 +02:00
|
|
|
// AIX
|
|
|
|
#if defined(_AIX)
|
|
|
|
# if !defined(isfinite)
|
|
|
|
# define isfinite finite
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// HP-UX
|
|
|
|
#if defined(__hpux)
|
|
|
|
# if !defined(isfinite)
|
2020-02-01 23:06:01 +01:00
|
|
|
# if defined(__ia64) && !defined(finite) && !defined(__GNUC__)
|
2015-08-17 11:37:30 +02:00
|
|
|
# define isfinite(x) ((sizeof(x) == sizeof(float) ? \
|
|
|
|
_Isfinitef(x) : _Isfinite(x)))
|
|
|
|
# else
|
2020-02-01 23:06:01 +01:00
|
|
|
# include <math.h>
|
2015-08-17 11:37:30 +02:00
|
|
|
# define isfinite finite
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
2015-04-27 22:25:09 +02:00
|
|
|
// 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 {
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
|
|
|
typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
|
|
|
|
#else
|
|
|
|
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
|
|
|
|
#endif
|
|
|
|
|
2015-04-27 22:25:09 +02:00
|
|
|
static bool containsControlCharacter(const char* str) {
|
|
|
|
while (*str) {
|
|
|
|
if (isControlCharacter(*(str++)))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
static bool containsControlCharacter0(const char* str, unsigned len) {
|
|
|
|
char const* end = str + len;
|
|
|
|
while (end != str) {
|
|
|
|
if (isControlCharacter(*str) || 0==*str)
|
|
|
|
return true;
|
|
|
|
++str;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSONCPP_STRING valueToString(LargestInt value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
UIntToStringBuffer buffer;
|
|
|
|
char* current = buffer + sizeof(buffer);
|
2018-01-26 17:06:56 +01:00
|
|
|
if (value == Value::minLargestInt) {
|
|
|
|
uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
|
2015-04-27 22:25:09 +02:00
|
|
|
*--current = '-';
|
2018-01-26 17:06:56 +01:00
|
|
|
} else if (value < 0) {
|
|
|
|
uintToString(LargestUInt(-value), current);
|
|
|
|
*--current = '-';
|
|
|
|
} else {
|
|
|
|
uintToString(LargestUInt(value), current);
|
|
|
|
}
|
2015-04-27 22:25:09 +02:00
|
|
|
assert(current >= buffer);
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING valueToString(LargestUInt value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
UIntToStringBuffer buffer;
|
|
|
|
char* current = buffer + sizeof(buffer);
|
|
|
|
uintToString(value, current);
|
|
|
|
assert(current >= buffer);
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(JSON_HAS_INT64)
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING valueToString(Int value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
return valueToString(LargestInt(value));
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING valueToString(UInt value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
return valueToString(LargestUInt(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // # if defined(JSON_HAS_INT64)
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
namespace {
|
|
|
|
JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
|
2015-04-27 22:25:09 +02:00
|
|
|
// Allocate a buffer that is more than large enough to store the 16 digits of
|
|
|
|
// precision requested below.
|
2018-01-26 17:06:56 +01:00
|
|
|
char buffer[36];
|
2015-04-27 22:25:09 +02:00
|
|
|
int len = -1;
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
char formatString[15];
|
|
|
|
snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
|
|
|
|
|
|
|
|
// 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.
|
2015-04-27 22:25:09 +02:00
|
|
|
if (isfinite(value)) {
|
2018-01-26 17:06:56 +01:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2015-04-27 22:25:09 +02:00
|
|
|
} else {
|
|
|
|
// IEEE standard states that NaN values will not compare to themselves
|
|
|
|
if (value != value) {
|
2018-01-26 17:06:56 +01:00
|
|
|
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
|
2015-04-27 22:25:09 +02:00
|
|
|
} else if (value < 0) {
|
2018-01-26 17:06:56 +01:00
|
|
|
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
|
2015-04-27 22:25:09 +02:00
|
|
|
} else {
|
2018-01-26 17:06:56 +01:00
|
|
|
len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(len >= 0);
|
|
|
|
return buffer;
|
|
|
|
}
|
2018-01-26 17:06:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
|
2015-04-27 22:25:09 +02:00
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
|
2015-04-27 22:25:09 +02:00
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING valueToQuotedString(const char* value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
if (value == NULL)
|
|
|
|
return "";
|
|
|
|
// Not sure how to handle unicode...
|
|
|
|
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
|
|
|
|
!containsControlCharacter(value))
|
2018-01-26 17:06:56 +01:00
|
|
|
return JSONCPP_STRING("\"") + value + "\"";
|
2015-04-27 22:25:09 +02:00
|
|
|
// We have to walk value and escape any special characters.
|
2018-01-26 17:06:56 +01:00
|
|
|
// Appending to JSONCPP_STRING is not efficient, but this should be rare.
|
2015-04-27 22:25:09 +02:00
|
|
|
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING::size_type maxsize =
|
2015-04-27 22:25:09 +02:00
|
|
|
strlen(value) * 2 + 3; // allescaped+quotes+NULL
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING result;
|
2015-04-27 22:25:09 +02:00
|
|
|
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)) {
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
|
|
|
|
static char const* strnpbrk(char const* s, char const* accept, size_t n) {
|
|
|
|
assert((s || !n) && accept);
|
|
|
|
|
|
|
|
char const* const end = s + n;
|
|
|
|
for (char const* cur = s; cur < end; ++cur) {
|
|
|
|
int const c = *cur;
|
|
|
|
for (char const* a = accept; *a; ++a) {
|
|
|
|
if (*a == c) {
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {
|
|
|
|
if (value == NULL)
|
|
|
|
return "";
|
|
|
|
// Not sure how to handle unicode...
|
|
|
|
if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
|
|
|
|
!containsControlCharacter0(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 </
|
|
|
|
// sequence.
|
|
|
|
// Should add a flag to allow this compatibility mode and prevent this
|
|
|
|
// sequence from occurring.
|
|
|
|
default:
|
|
|
|
if ((isControlCharacter(*c)) || (*c == 0)) {
|
|
|
|
JSONCPP_OSTRINGSTREAM oss;
|
2015-04-27 22:25:09 +02:00
|
|
|
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; }
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING FastWriter::write(const Value& root) {
|
|
|
|
document_.clear();
|
2015-04-27 22:25:09 +02:00
|
|
|
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:
|
2018-01-26 17:06:56 +01:00
|
|
|
{
|
|
|
|
// 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<unsigned>(end-str));
|
2015-04-27 22:25:09 +02:00
|
|
|
break;
|
2018-01-26 17:06:56 +01:00
|
|
|
}
|
2015-04-27 22:25:09 +02:00
|
|
|
case booleanValue:
|
|
|
|
document_ += valueToString(value.asBool());
|
|
|
|
break;
|
|
|
|
case arrayValue: {
|
|
|
|
document_ += '[';
|
2018-01-26 17:06:56 +01:00
|
|
|
ArrayIndex size = value.size();
|
|
|
|
for (ArrayIndex index = 0; index < size; ++index) {
|
2015-04-27 22:25:09 +02:00
|
|
|
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) {
|
2018-01-26 17:06:56 +01:00
|
|
|
const JSONCPP_STRING& name = *it;
|
2015-04-27 22:25:09 +02:00
|
|
|
if (it != members.begin())
|
|
|
|
document_ += ',';
|
2018-01-26 17:06:56 +01:00
|
|
|
document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
|
2015-04-27 22:25:09 +02:00
|
|
|
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
|
|
|
|
writeValue(value[name]);
|
|
|
|
}
|
|
|
|
document_ += '}';
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Class StyledWriter
|
|
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
StyledWriter::StyledWriter()
|
|
|
|
: rightMargin_(74), indentSize_(3), addChildValues_() {}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_STRING StyledWriter::write(const Value& root) {
|
|
|
|
document_.clear();
|
2015-04-27 22:25:09 +02:00
|
|
|
addChildValues_ = false;
|
2018-01-26 17:06:56 +01:00
|
|
|
indentString_.clear();
|
2015-04-27 22:25:09 +02:00
|
|
|
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:
|
2018-01-26 17:06:56 +01:00
|
|
|
{
|
|
|
|
// 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<unsigned>(end-str)));
|
|
|
|
else pushValue("");
|
2015-04-27 22:25:09 +02:00
|
|
|
break;
|
2018-01-26 17:06:56 +01:00
|
|
|
}
|
2015-04-27 22:25:09 +02:00
|
|
|
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 (;;) {
|
2018-01-26 17:06:56 +01:00
|
|
|
const JSONCPP_STRING& name = *it;
|
2015-04-27 22:25:09 +02:00
|
|
|
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) {
|
2018-01-26 17:06:56 +01:00
|
|
|
ArrayIndex const size = value.size();
|
2015-04-27 22:25:09 +02:00
|
|
|
bool isMultiLine = size * 3 >= rightMargin_;
|
|
|
|
childValues_.clear();
|
2018-01-26 17:06:56 +01:00
|
|
|
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
|
2015-04-27 22:25:09 +02:00
|
|
|
const Value& childValue = value[index];
|
2018-01-26 17:06:56 +01:00
|
|
|
isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
|
2015-04-27 22:25:09 +02:00
|
|
|
childValue.size() > 0);
|
|
|
|
}
|
|
|
|
if (!isMultiLine) // check if line length > max line length
|
|
|
|
{
|
|
|
|
childValues_.reserve(size);
|
|
|
|
addChildValues_ = true;
|
2018-01-26 17:06:56 +01:00
|
|
|
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
|
|
|
for (ArrayIndex index = 0; index < size; ++index) {
|
|
|
|
if (hasCommentForValue(value[index])) {
|
|
|
|
isMultiLine = true;
|
|
|
|
}
|
2015-04-27 22:25:09 +02:00
|
|
|
writeValue(value[index]);
|
2018-01-26 17:06:56 +01:00
|
|
|
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
addChildValues_ = false;
|
|
|
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
|
|
|
}
|
|
|
|
return isMultiLine;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
void StyledWriter::pushValue(const JSONCPP_STRING& value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
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_;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
writeIndent();
|
|
|
|
document_ += value;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }
|
2015-04-27 22:25:09 +02:00
|
|
|
|
|
|
|
void StyledWriter::unindent() {
|
2018-01-26 17:06:56 +01:00
|
|
|
assert(indentString_.size() >= indentSize_);
|
2015-04-27 22:25:09 +02:00
|
|
|
indentString_.resize(indentString_.size() - indentSize_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StyledWriter::writeCommentBeforeValue(const Value& root) {
|
|
|
|
if (!root.hasComment(commentBefore))
|
|
|
|
return;
|
|
|
|
|
|
|
|
document_ += "\n";
|
|
|
|
writeIndent();
|
2018-01-26 17:06:56 +01:00
|
|
|
const JSONCPP_STRING& comment = root.getComment(commentBefore);
|
|
|
|
JSONCPP_STRING::const_iterator iter = comment.begin();
|
|
|
|
while (iter != comment.end()) {
|
2015-04-27 22:25:09 +02:00
|
|
|
document_ += *iter;
|
2018-01-26 17:06:56 +01:00
|
|
|
if (*iter == '\n' &&
|
|
|
|
(iter != comment.end() && *(iter + 1) == '/'))
|
2015-04-27 22:25:09 +02:00
|
|
|
writeIndent();
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
// Comments are stripped of trailing newlines, so add one here
|
2015-04-27 22:25:09 +02:00
|
|
|
document_ += "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
|
|
|
|
if (root.hasComment(commentAfterOnSameLine))
|
2018-01-26 17:06:56 +01:00
|
|
|
document_ += " " + root.getComment(commentAfterOnSameLine);
|
2015-04-27 22:25:09 +02:00
|
|
|
|
|
|
|
if (root.hasComment(commentAfter)) {
|
|
|
|
document_ += "\n";
|
2018-01-26 17:06:56 +01:00
|
|
|
document_ += root.getComment(commentAfter);
|
2015-04-27 22:25:09 +02:00
|
|
|
document_ += "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StyledWriter::hasCommentForValue(const Value& value) {
|
|
|
|
return value.hasComment(commentBefore) ||
|
|
|
|
value.hasComment(commentAfterOnSameLine) ||
|
|
|
|
value.hasComment(commentAfter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Class StyledStreamWriter
|
|
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)
|
2015-04-27 22:25:09 +02:00
|
|
|
: document_(NULL), rightMargin_(74), indentation_(indentation),
|
|
|
|
addChildValues_() {}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {
|
2015-04-27 22:25:09 +02:00
|
|
|
document_ = &out;
|
|
|
|
addChildValues_ = false;
|
2018-01-26 17:06:56 +01:00
|
|
|
indentString_.clear();
|
|
|
|
indented_ = true;
|
2015-04-27 22:25:09 +02:00
|
|
|
writeCommentBeforeValue(root);
|
2018-01-26 17:06:56 +01:00
|
|
|
if (!indented_) writeIndent();
|
|
|
|
indented_ = true;
|
2015-04-27 22:25:09 +02:00
|
|
|
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:
|
2018-01-26 17:06:56 +01:00
|
|
|
{
|
|
|
|
// 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<unsigned>(end-str)));
|
|
|
|
else pushValue("");
|
2015-04-27 22:25:09 +02:00
|
|
|
break;
|
2018-01-26 17:06:56 +01:00
|
|
|
}
|
2015-04-27 22:25:09 +02:00
|
|
|
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 (;;) {
|
2018-01-26 17:06:56 +01:00
|
|
|
const JSONCPP_STRING& name = *it;
|
2015-04-27 22:25:09 +02:00
|
|
|
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 {
|
2018-01-26 17:06:56 +01:00
|
|
|
if (!indented_) writeIndent();
|
|
|
|
indented_ = true;
|
2015-04-27 22:25:09 +02:00
|
|
|
writeValue(childValue);
|
2018-01-26 17:06:56 +01:00
|
|
|
indented_ = false;
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
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) {
|
2018-01-26 17:06:56 +01:00
|
|
|
ArrayIndex const size = value.size();
|
2015-04-27 22:25:09 +02:00
|
|
|
bool isMultiLine = size * 3 >= rightMargin_;
|
|
|
|
childValues_.clear();
|
2018-01-26 17:06:56 +01:00
|
|
|
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
|
2015-04-27 22:25:09 +02:00
|
|
|
const Value& childValue = value[index];
|
2018-01-26 17:06:56 +01:00
|
|
|
isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
|
2015-04-27 22:25:09 +02:00
|
|
|
childValue.size() > 0);
|
|
|
|
}
|
|
|
|
if (!isMultiLine) // check if line length > max line length
|
|
|
|
{
|
|
|
|
childValues_.reserve(size);
|
|
|
|
addChildValues_ = true;
|
2018-01-26 17:06:56 +01:00
|
|
|
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
|
|
|
for (ArrayIndex index = 0; index < size; ++index) {
|
|
|
|
if (hasCommentForValue(value[index])) {
|
|
|
|
isMultiLine = true;
|
|
|
|
}
|
2015-04-27 22:25:09 +02:00
|
|
|
writeValue(value[index]);
|
2018-01-26 17:06:56 +01:00
|
|
|
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
addChildValues_ = false;
|
|
|
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
|
|
|
}
|
|
|
|
return isMultiLine;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {
|
2015-04-27 22:25:09 +02:00
|
|
|
if (addChildValues_)
|
|
|
|
childValues_.push_back(value);
|
|
|
|
else
|
|
|
|
*document_ << value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StyledStreamWriter::writeIndent() {
|
2018-01-26 17:06:56 +01:00
|
|
|
// 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_.
|
2015-04-27 22:25:09 +02:00
|
|
|
*document_ << '\n' << indentString_;
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {
|
|
|
|
if (!indented_) writeIndent();
|
2015-04-27 22:25:09 +02:00
|
|
|
*document_ << value;
|
2018-01-26 17:06:56 +01:00
|
|
|
indented_ = false;
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2018-01-26 17:06:56 +01:00
|
|
|
|
|
|
|
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 != comment.end() && *(iter + 1) == '/'))
|
|
|
|
// writeIndent(); // would include newline
|
|
|
|
*document_ << indentString_;
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
indented_ = false;
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
|
|
|
|
if (root.hasComment(commentAfterOnSameLine))
|
2018-01-26 17:06:56 +01:00
|
|
|
*document_ << ' ' << root.getComment(commentAfterOnSameLine);
|
2015-04-27 22:25:09 +02:00
|
|
|
|
|
|
|
if (root.hasComment(commentAfter)) {
|
2018-01-26 17:06:56 +01:00
|
|
|
writeIndent();
|
|
|
|
*document_ << root.getComment(commentAfter);
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
2018-01-26 17:06:56 +01:00
|
|
|
indented_ = false;
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
|
|
|
|
return value.hasComment(commentBefore) ||
|
|
|
|
value.hasComment(commentAfterOnSameLine) ||
|
|
|
|
value.hasComment(commentAfter);
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
//////////////////////////
|
|
|
|
// 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 isMultineArray(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<JSONCPP_STRING> 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<unsigned>(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<unsigned>(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) || isMultineArray(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
|
2015-04-27 22:25:09 +02:00
|
|
|
{
|
2018-01-26 17:06:56 +01:00
|
|
|
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::isMultineArray(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<ArrayIndex>(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 != comment.end() && *(iter + 1) == '/'))
|
|
|
|
// writeIndent(); // would write extra newline
|
|
|
|
*sout_ << indentString_;
|
|
|
|
++iter;
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
2018-01-26 17:06:56 +01:00
|
|
|
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<JSONCPP_STRING>* 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<JSONCPP_STRING> 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();
|
2015-04-27 22:25:09 +02:00
|
|
|
}
|
|
|
|
|
2018-01-26 17:06:56 +01:00
|
|
|
JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {
|
|
|
|
StreamWriterBuilder builder;
|
|
|
|
StreamWriterPtr const writer(builder.newStreamWriter());
|
|
|
|
writer->write(root, &sout);
|
2015-04-27 22:25:09 +02:00
|
|
|
return sout;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Json
|