Add setting precision for json writers and also add decimal places precision type. (#752)
* Added setting precision for writers.
* Added special case for precise precision and global precision.
* Added good setting of type of precision and also added this type to BuiltStreamWriter and for its settings.
* Added some tests.
diff --git a/include/json/value.h b/include/json/value.h
index 2643782..3474cd8 100644
--- a/include/json/value.h
+++ b/include/json/value.h
@@ -109,6 +109,13 @@
numberOfCommentPlacement
};
+/** \brief Type of precision for formatting of real values.
+ */
+enum PrecisionType {
+ significantDigits = 0, ///< we set max number of significant digits in string
+ decimalPlaces ///< we set max number of digits after "." in string
+};
+
//# ifdef JSON_USE_CPPTL
// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
// typedef CppTL::AnyEnumerator<const Value &> EnumValues;
@@ -220,6 +227,9 @@
static const UInt64 maxUInt64;
#endif // defined(JSON_HAS_INT64)
+ /// Default precision for real value for string representation.
+ static const UInt defaultRealPrecision;
+
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486
diff --git a/include/json/writer.h b/include/json/writer.h
index 35ec396..767b3bd 100644
--- a/include/json/writer.h
+++ b/include/json/writer.h
@@ -106,6 +106,10 @@
- If true, outputs non-finite floating point values in the following way:
NaN values as "NaN", positive infinity as "Infinity", and negative infinity
as "-Infinity".
+ - "precision": int
+ - Number of precision digits for formatting of real values.
+ - "precisionType": "significant"(default) or "decimal"
+ - Type of precision for formatting of real values.
You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any
@@ -339,7 +343,8 @@
#endif // if defined(JSON_HAS_INT64)
JSONCPP_STRING JSON_API valueToString(LargestInt value);
JSONCPP_STRING JSON_API valueToString(LargestUInt value);
-JSONCPP_STRING JSON_API valueToString(double value);
+JSONCPP_STRING JSON_API valueToString(double value, unsigned int precision = Value::defaultRealPrecision,
+ PrecisionType precisionType = PrecisionType::significantDigits);
JSONCPP_STRING JSON_API valueToString(bool value);
JSONCPP_STRING JSON_API valueToQuotedString(const char* value);
diff --git a/src/lib_json/json_tool.h b/src/lib_json/json_tool.h
index 4590bfc..86f1f2c 100644
--- a/src/lib_json/json_tool.h
+++ b/src/lib_json/json_tool.h
@@ -109,6 +109,20 @@
}
}
+/**
+ * Delete zeros in the end of string, if it isn't last zero before '.' character.
+ */
+static inline void fixZerosInTheEnd(char* begin, char* end) {
+ end--;
+ while ((begin < end) && (*end == '0')) {
+ // don't delete last zero before point.
+ if (*(end - 1) != '.') {
+ *end = '\0';
+ }
+ end--;
+ }
+}
+
} // namespace Json {
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp
index 1633f49..ed49df2 100644
--- a/src/lib_json/json_value.cpp
+++ b/src/lib_json/json_value.cpp
@@ -64,6 +64,8 @@
const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+const UInt Value::defaultRealPrecision = 17;
+
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U>
static inline bool InRange(double d, T min, U max) {
diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp
index 5167c31..be03a66 100644
--- a/src/lib_json/json_writer.cpp
+++ b/src/lib_json/json_writer.cpp
@@ -118,14 +118,18 @@
#endif // # if defined(JSON_HAS_INT64)
namespace {
-JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
+JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision, PrecisionType precisionType) {
// Allocate a buffer that is more than large enough to store the 16 digits of
// precision requested below.
char buffer[36];
int len = -1;
char formatString[15];
- snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
+ if (precisionType == PrecisionType::significantDigits) {
+ snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
+ } else {
+ snprintf(formatString, sizeof(formatString), "%%.%uf", precision);
+ }
// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distinguish the
@@ -133,6 +137,10 @@
if (isfinite(value)) {
len = snprintf(buffer, sizeof(buffer), formatString, value);
fixNumericLocale(buffer, buffer + len);
+ // to delete use-less too much zeros in the end of string
+ if (precisionType == PrecisionType::decimalPlaces) {
+ fixZerosInTheEnd(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')) {
@@ -154,7 +162,9 @@
}
}
-JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
+JSONCPP_STRING valueToString(double value, unsigned int precision, PrecisionType precisionType) {
+ return valueToString(value, false, precision, precisionType);
+}
JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
@@ -856,7 +866,8 @@
JSONCPP_STRING const& nullSymbol,
JSONCPP_STRING const& endingLineFeedSymbol,
bool useSpecialFloats,
- unsigned int precision);
+ unsigned int precision,
+ PrecisionType precisionType);
int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
private:
void writeValue(Value const& value);
@@ -885,6 +896,7 @@
bool indented_ : 1;
bool useSpecialFloats_ : 1;
unsigned int precision_;
+ PrecisionType precisionType_;
};
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
JSONCPP_STRING const& indentation,
@@ -893,7 +905,8 @@
JSONCPP_STRING const& nullSymbol,
JSONCPP_STRING const& endingLineFeedSymbol,
bool useSpecialFloats,
- unsigned int precision)
+ unsigned int precision,
+ PrecisionType precisionType)
: rightMargin_(74)
, indentation_(indentation)
, cs_(cs)
@@ -904,6 +917,7 @@
, indented_(false)
, useSpecialFloats_(useSpecialFloats)
, precision_(precision)
+ , precisionType_(precisionType)
{
}
int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
@@ -933,7 +947,7 @@
pushValue(valueToString(value.asLargestUInt()));
break;
case realValue:
- pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
+ pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, precisionType_));
break;
case stringValue:
{
@@ -1145,6 +1159,7 @@
{
JSONCPP_STRING indentation = settings_["indentation"].asString();
JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
+ JSONCPP_STRING pt_str = settings_["precisionType"].asString();
bool eyc = settings_["enableYAMLCompatibility"].asBool();
bool dnp = settings_["dropNullPlaceholders"].asBool();
bool usf = settings_["useSpecialFloats"].asBool();
@@ -1157,6 +1172,14 @@
} 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'");
+ }
JSONCPP_STRING colonSymbol = " : ";
if (eyc) {
colonSymbol = ": ";
@@ -1171,7 +1194,7 @@
JSONCPP_STRING endingLineFeedSymbol;
return new BuiltStyledStreamWriter(
indentation, cs,
- colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
+ colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre, precisionType);
}
static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
{
@@ -1182,6 +1205,7 @@
valid_keys->insert("dropNullPlaceholders");
valid_keys->insert("useSpecialFloats");
valid_keys->insert("precision");
+ valid_keys->insert("precisionType");
}
bool StreamWriterBuilder::validate(Json::Value* invalid) const
{
@@ -1214,6 +1238,7 @@
(*settings)["dropNullPlaceholders"] = false;
(*settings)["useSpecialFloats"] = false;
(*settings)["precision"] = 17;
+ (*settings)["precisionType"] = "significant";
//! [StreamWriterBuilderDefaults]
}
diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp
index ee81790..cbf4823 100644
--- a/src/test_lib_json/main.cpp
+++ b/src/test_lib_json/main.cpp
@@ -1757,6 +1757,27 @@
expected = "0.25634569487374054";
result = Json::writeString(b, v);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+
+ b.settings_["precision"] = 5;
+ b.settings_["precisionType"] = "decimal";
+ v = 0.256345694873740545068;
+ expected = "0.25635";
+ result = Json::writeString(b, v);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+
+ b.settings_["precision"] = 1;
+ b.settings_["precisionType"] = "decimal";
+ v = 0.256345694873740545068;
+ expected = "0.3";
+ result = Json::writeString(b, v);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+
+ b.settings_["precision"] = 10;
+ b.settings_["precisionType"] = "decimal";
+ v = 0.23300000;
+ expected = "0.233";
+ result = Json::writeString(b, v);
+ JSONTEST_ASSERT_STRING_EQUAL(expected, result);
}
struct WriterTest : JsonTest::TestCase {};