mirror of
https://github.com/nlohmann/json.git
synced 2024-08-24 14:02:29 +02:00
Merge pull request #232 from nlohmann/issue228
make serialization locale-independent (fixes #228)
This commit is contained in:
commit
170b70f082
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;
|
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:
|
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
|
// check if number was parsed from a string
|
||||||
if (m_type.bits.parsed)
|
if (m_type.bits.parsed)
|
||||||
{
|
{
|
||||||
// check if parsed number had an exponent given
|
// check if parsed number had an exponent given
|
||||||
if (m_type.bits.has_exp)
|
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
|
// handle capitalization of the exponent
|
||||||
if (m_type.bits.exp_cap)
|
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
|
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
|
// remove '+' sign from the exponent if necessary
|
||||||
|
@ -6152,40 +6167,40 @@ class basic_json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o << buf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no exponent - output as a decimal
|
// no exponent - output as a decimal
|
||||||
snprintf(buf, sizeof(buf), "%.*f",
|
std::stringstream ss;
|
||||||
m_type.bits.precision, m_value.number_float);
|
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
|
else
|
||||||
{
|
{
|
||||||
// Otherwise 6, 15 or 16 digits of precision allows
|
if (m_value.number_float == 0)
|
||||||
// round-trip IEEE 754 string->float->string,
|
{
|
||||||
// string->double->string or string->long double->string;
|
// special case for zero to get "0.0"/"-0.0"
|
||||||
// to be safe, we read this value from
|
o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
|
||||||
// std::numeric_limits<number_float_t>::digits10
|
}
|
||||||
snprintf(buf, sizeof(buf), "%.*g",
|
else
|
||||||
std::numeric_limits<double>::digits10,
|
{
|
||||||
m_value.number_float);
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,19 @@ struct has_mapped_type
|
||||||
static constexpr bool value = sizeof(test<T>(0)) == 1;
|
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:
|
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
|
// check if number was parsed from a string
|
||||||
if (m_type.bits.parsed)
|
if (m_type.bits.parsed)
|
||||||
{
|
{
|
||||||
// check if parsed number had an exponent given
|
// check if parsed number had an exponent given
|
||||||
if (m_type.bits.has_exp)
|
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
|
// handle capitalization of the exponent
|
||||||
if (m_type.bits.exp_cap)
|
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
|
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
|
// remove '+' sign from the exponent if necessary
|
||||||
|
@ -6152,40 +6167,40 @@ class basic_json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o << buf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no exponent - output as a decimal
|
// no exponent - output as a decimal
|
||||||
snprintf(buf, sizeof(buf), "%.*f",
|
std::stringstream ss;
|
||||||
m_type.bits.precision, m_value.number_float);
|
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
|
else
|
||||||
{
|
{
|
||||||
// Otherwise 6, 15 or 16 digits of precision allows
|
if (m_value.number_float == 0)
|
||||||
// round-trip IEEE 754 string->float->string,
|
{
|
||||||
// string->double->string or string->long double->string;
|
// special case for zero to get "0.0"/"-0.0"
|
||||||
// to be safe, we read this value from
|
o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
|
||||||
// std::numeric_limits<number_float_t>::digits10
|
}
|
||||||
snprintf(buf, sizeof(buf), "%.*g",
|
else
|
||||||
std::numeric_limits<double>::digits10,
|
{
|
||||||
m_value.number_float);
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12369,5 +12369,43 @@ TEST_CASE("regression tests")
|
||||||
j_long_double = 1.23e45L;
|
j_long_double = 1.23e45L;
|
||||||
CHECK(j_long_double.get<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