Merge pull request #232 from nlohmann/issue228
make serialization locale-independent (fixes #228)
This commit is contained in:
commit
170b70f082
3 changed files with 130 additions and 62 deletions
77
src/json.hpp
77
src/json.hpp
|
@ -88,6 +88,19 @@ struct has_mapped_type
|
|||
static constexpr bool value = sizeof(test<T>(0)) == 1;
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief helper class to create locales with decimal point
|
||||
@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
|
||||
*/
|
||||
class DecimalSeparator : public std::numpunct<char>
|
||||
{
|
||||
protected:
|
||||
char do_decimal_point() const
|
||||
{
|
||||
return '.';
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -6114,24 +6127,26 @@ class basic_json
|
|||
|
||||
case value_t::number_float:
|
||||
{
|
||||
// buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
|
||||
char buf[263];
|
||||
int len;
|
||||
|
||||
// check if number was parsed from a string
|
||||
if (m_type.bits.parsed)
|
||||
{
|
||||
// check if parsed number had an exponent given
|
||||
if (m_type.bits.has_exp)
|
||||
{
|
||||
// buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
|
||||
char buf[263];
|
||||
int len;
|
||||
|
||||
// handle capitalization of the exponent
|
||||
if (m_type.bits.exp_cap)
|
||||
{
|
||||
len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision, m_value.number_float) + 1;
|
||||
len = snprintf(buf, sizeof(buf), "%.*E",
|
||||
m_type.bits.precision, m_value.number_float) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision, m_value.number_float) + 1;
|
||||
len = snprintf(buf, sizeof(buf), "%.*e",
|
||||
m_type.bits.precision, m_value.number_float) + 1;
|
||||
}
|
||||
|
||||
// remove '+' sign from the exponent if necessary
|
||||
|
@ -6152,40 +6167,40 @@ class basic_json
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
o << buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no exponent - output as a decimal
|
||||
snprintf(buf, sizeof(buf), "%.*f",
|
||||
m_type.bits.precision, m_value.number_float);
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
|
||||
ss << std::setprecision(m_type.bits.precision)
|
||||
<< std::fixed << m_value.number_float;
|
||||
o << ss.str();
|
||||
}
|
||||
}
|
||||
else if (m_value.number_float == 0)
|
||||
{
|
||||
// special case for zero to get "0.0"/"-0.0"
|
||||
if (std::signbit(m_value.number_float))
|
||||
{
|
||||
o << "-0.0";
|
||||
}
|
||||
else
|
||||
{
|
||||
o << "0.0";
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise 6, 15 or 16 digits of precision allows
|
||||
// round-trip IEEE 754 string->float->string,
|
||||
// string->double->string or string->long double->string;
|
||||
// to be safe, we read this value from
|
||||
// std::numeric_limits<number_float_t>::digits10
|
||||
snprintf(buf, sizeof(buf), "%.*g",
|
||||
std::numeric_limits<double>::digits10,
|
||||
m_value.number_float);
|
||||
if (m_value.number_float == 0)
|
||||
{
|
||||
// special case for zero to get "0.0"/"-0.0"
|
||||
o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise 6, 15 or 16 digits of precision allows
|
||||
// round-trip IEEE 754 string->float->string,
|
||||
// string->double->string or string->long double->string;
|
||||
// to be safe, we read this value from
|
||||
// std::numeric_limits<number_float_t>::digits10
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
|
||||
ss << std::setprecision(std::numeric_limits<double>::digits10)
|
||||
<< m_value.number_float;
|
||||
o << ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
o << buf;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,19 @@ struct has_mapped_type
|
|||
static constexpr bool value = sizeof(test<T>(0)) == 1;
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief helper class to create locales with decimal point
|
||||
@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
|
||||
*/
|
||||
class DecimalSeparator : public std::numpunct<char>
|
||||
{
|
||||
protected:
|
||||
char do_decimal_point() const
|
||||
{
|
||||
return '.';
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -6114,24 +6127,26 @@ class basic_json
|
|||
|
||||
case value_t::number_float:
|
||||
{
|
||||
// buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
|
||||
char buf[263];
|
||||
int len;
|
||||
|
||||
// check if number was parsed from a string
|
||||
if (m_type.bits.parsed)
|
||||
{
|
||||
// check if parsed number had an exponent given
|
||||
if (m_type.bits.has_exp)
|
||||
{
|
||||
// buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
|
||||
char buf[263];
|
||||
int len;
|
||||
|
||||
// handle capitalization of the exponent
|
||||
if (m_type.bits.exp_cap)
|
||||
{
|
||||
len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision, m_value.number_float) + 1;
|
||||
len = snprintf(buf, sizeof(buf), "%.*E",
|
||||
m_type.bits.precision, m_value.number_float) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision, m_value.number_float) + 1;
|
||||
len = snprintf(buf, sizeof(buf), "%.*e",
|
||||
m_type.bits.precision, m_value.number_float) + 1;
|
||||
}
|
||||
|
||||
// remove '+' sign from the exponent if necessary
|
||||
|
@ -6152,40 +6167,40 @@ class basic_json
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
o << buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no exponent - output as a decimal
|
||||
snprintf(buf, sizeof(buf), "%.*f",
|
||||
m_type.bits.precision, m_value.number_float);
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
|
||||
ss << std::setprecision(m_type.bits.precision)
|
||||
<< std::fixed << m_value.number_float;
|
||||
o << ss.str();
|
||||
}
|
||||
}
|
||||
else if (m_value.number_float == 0)
|
||||
{
|
||||
// special case for zero to get "0.0"/"-0.0"
|
||||
if (std::signbit(m_value.number_float))
|
||||
{
|
||||
o << "-0.0";
|
||||
}
|
||||
else
|
||||
{
|
||||
o << "0.0";
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise 6, 15 or 16 digits of precision allows
|
||||
// round-trip IEEE 754 string->float->string,
|
||||
// string->double->string or string->long double->string;
|
||||
// to be safe, we read this value from
|
||||
// std::numeric_limits<number_float_t>::digits10
|
||||
snprintf(buf, sizeof(buf), "%.*g",
|
||||
std::numeric_limits<double>::digits10,
|
||||
m_value.number_float);
|
||||
if (m_value.number_float == 0)
|
||||
{
|
||||
// special case for zero to get "0.0"/"-0.0"
|
||||
o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise 6, 15 or 16 digits of precision allows
|
||||
// round-trip IEEE 754 string->float->string,
|
||||
// string->double->string or string->long double->string;
|
||||
// to be safe, we read this value from
|
||||
// std::numeric_limits<number_float_t>::digits10
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
|
||||
ss << std::setprecision(std::numeric_limits<double>::digits10)
|
||||
<< m_value.number_float;
|
||||
o << ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
o << buf;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -12369,5 +12369,43 @@ TEST_CASE("regression tests")
|
|||
j_long_double = 1.23e45L;
|
||||
CHECK(j_long_double.get<long double>() == 1.23e45L);
|
||||
}
|
||||
|
||||
SECTION("issue #228 - double values are serialized with commas as decimal points")
|
||||
{
|
||||
json j1a = 23.42;
|
||||
json j1b = json::parse("23.42");
|
||||
|
||||
json j2a = 2342e-2;
|
||||
//issue #230
|
||||
//json j2b = json::parse("2342e-2");
|
||||
|
||||
json j3a = 10E3;
|
||||
json j3b = json::parse("10E3");
|
||||
json j3c = json::parse("10e3");
|
||||
|
||||
// class to create a locale that would use a comma for decimals
|
||||
class CommaDecimalSeparator : public std::numpunct<char>
|
||||
{
|
||||
protected:
|
||||
char do_decimal_point() const
|
||||
{
|
||||
return ',';
|
||||
}
|
||||
};
|
||||
|
||||
// change locale to mess with decimal points
|
||||
std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator));
|
||||
|
||||
CHECK(j1a.dump() == "23.42");
|
||||
CHECK(j1b.dump() == "23.42");
|
||||
|
||||
CHECK(j2a.dump() == "23.42");
|
||||
//issue #230
|
||||
//CHECK(j2b.dump() == "23.42");
|
||||
|
||||
CHECK(j3a.dump() == "10000");
|
||||
CHECK(j3b.dump() == "1E04");
|
||||
CHECK(j3c.dump() == "1e04");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue