mirror of
https://github.com/nlohmann/json.git
synced 2026-09-07 16:57:59 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e59b455616 | ||
|
|
e29ac4f043 |
@@ -4,8 +4,8 @@
|
|||||||
Hello, world!
|
Hello, world!
|
||||||
1 2 3 4 5
|
1 2 3 4 5
|
||||||
|
|
||||||
string: "Hello, world!"
|
|
||||||
number: {"floating-point":17.23,"integer":42}
|
number: {"floating-point":17.23,"integer":42}
|
||||||
null: null
|
null: null
|
||||||
|
string: "Hello, world!"
|
||||||
boolean: true
|
boolean: true
|
||||||
array: [1,2,3,4,5]
|
array: [1,2,3,4,5]
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
Hello, world!
|
Hello, world!
|
||||||
1 2 3 4 5
|
1 2 3 4 5
|
||||||
|
|
||||||
string: "Hello, world!"
|
|
||||||
number: {"floating-point":17.23,"integer":42}
|
number: {"floating-point":17.23,"integer":42}
|
||||||
null: null
|
null: null
|
||||||
|
string: "Hello, world!"
|
||||||
boolean: true
|
boolean: true
|
||||||
array: [1,2,3,4,5]
|
array: [1,2,3,4,5]
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
Hello, world!
|
Hello, world!
|
||||||
1 2 3 4 5
|
1 2 3 4 5
|
||||||
|
|
||||||
string: "Hello, world!"
|
|
||||||
number: {"floating-point":17.23,"integer":42}
|
number: {"floating-point":17.23,"integer":42}
|
||||||
null: null
|
null: null
|
||||||
|
string: "Hello, world!"
|
||||||
boolean: true
|
boolean: true
|
||||||
array: [1,2,3,4,5]
|
array: [1,2,3,4,5]
|
||||||
[json.exception.type_error.302] type must be boolean, but is string
|
[json.exception.type_error.302] type must be boolean, but is string
|
||||||
|
|||||||
@@ -398,6 +398,34 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename ConstructibleObjectType>
|
||||||
|
auto from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<1> /*unused*/)
|
||||||
|
-> decltype(
|
||||||
|
obj.reserve(std::declval<typename ConstructibleObjectType::size_type>()),
|
||||||
|
void())
|
||||||
|
{
|
||||||
|
ConstructibleObjectType ret;
|
||||||
|
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||||
|
ret.reserve(inner_object->size());
|
||||||
|
for (const auto& p : *inner_object)
|
||||||
|
{
|
||||||
|
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
||||||
|
}
|
||||||
|
obj = std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename ConstructibleObjectType>
|
||||||
|
inline void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<0> /*unused*/)
|
||||||
|
{
|
||||||
|
ConstructibleObjectType ret;
|
||||||
|
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||||
|
for (const auto& p : *inner_object)
|
||||||
|
{
|
||||||
|
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
||||||
|
}
|
||||||
|
obj = std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||||
@@ -407,13 +435,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
|||||||
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstructibleObjectType ret;
|
from_json_object_impl(j, obj, priority_tag<1> {});
|
||||||
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
|
||||||
for (const auto& p : *inner_object)
|
|
||||||
{
|
|
||||||
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
|
||||||
}
|
|
||||||
obj = std::move(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// overload for arithmetic types, not chosen for basic_json template arguments
|
// overload for arithmetic types, not chosen for basic_json template arguments
|
||||||
|
|||||||
@@ -1647,20 +1647,6 @@ class binary_writer
|
|||||||
return 'D'; // float 64
|
return 'D'; // float 64
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief checks whether a JSON number fits into @a TargetType
|
|
||||||
@param[in] el a JSON number of either the signed or unsigned integer kind
|
|
||||||
@return whether @a el's value can be represented by @a TargetType without
|
|
||||||
wrapping, regardless of which of the two kinds it is stored as
|
|
||||||
*/
|
|
||||||
template<typename TargetType>
|
|
||||||
static bool bjdata_ndarray_value_in_range(const BasicJsonType& el)
|
|
||||||
{
|
|
||||||
return el.is_number_unsigned()
|
|
||||||
? value_in_range_of<TargetType>(el.template get<std::uint64_t>())
|
|
||||||
: value_in_range_of<TargetType>(el.template get<std::int64_t>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
|
@return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
|
||||||
*/
|
*/
|
||||||
@@ -1745,60 +1731,6 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// every element is cast to the (possibly narrower) C++ type matching
|
|
||||||
// dtype below; a value that does not fit that type would silently
|
|
||||||
// wrap (integers) or overflow to infinity (the "single" precision
|
|
||||||
// float) instead of being reported, so such an object falls back to
|
|
||||||
// a plain object encoding as well
|
|
||||||
for (const auto& el : value.at(key))
|
|
||||||
{
|
|
||||||
bool in_range = true;
|
|
||||||
switch (dtype)
|
|
||||||
{
|
|
||||||
case 'U':
|
|
||||||
case 'C':
|
|
||||||
case 'B':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint8_t>(el);
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int8_t>(el);
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint16_t>(el);
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int16_t>(el);
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint32_t>(el);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int32_t>(el);
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint64_t>(el);
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int64_t>(el);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
{
|
|
||||||
const auto dval = el.template get<double>();
|
|
||||||
in_range = !std::isfinite(dval) ||
|
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// 'D' (double) already spans the full range of number_float_t
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!in_range)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
oa->write_character('[');
|
oa->write_character('[');
|
||||||
oa->write_character('$');
|
oa->write_character('$');
|
||||||
oa->write_character(dtype);
|
oa->write_character(dtype);
|
||||||
|
|||||||
@@ -5693,6 +5693,34 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename ConstructibleObjectType>
|
||||||
|
auto from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<1> /*unused*/)
|
||||||
|
-> decltype(
|
||||||
|
obj.reserve(std::declval<typename ConstructibleObjectType::size_type>()),
|
||||||
|
void())
|
||||||
|
{
|
||||||
|
ConstructibleObjectType ret;
|
||||||
|
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||||
|
ret.reserve(inner_object->size());
|
||||||
|
for (const auto& p : *inner_object)
|
||||||
|
{
|
||||||
|
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
||||||
|
}
|
||||||
|
obj = std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename ConstructibleObjectType>
|
||||||
|
inline void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<0> /*unused*/)
|
||||||
|
{
|
||||||
|
ConstructibleObjectType ret;
|
||||||
|
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||||
|
for (const auto& p : *inner_object)
|
||||||
|
{
|
||||||
|
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
||||||
|
}
|
||||||
|
obj = std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||||
@@ -5702,13 +5730,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
|||||||
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstructibleObjectType ret;
|
from_json_object_impl(j, obj, priority_tag<1> {});
|
||||||
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
|
||||||
for (const auto& p : *inner_object)
|
|
||||||
{
|
|
||||||
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
|
||||||
}
|
|
||||||
obj = std::move(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// overload for arithmetic types, not chosen for basic_json template arguments
|
// overload for arithmetic types, not chosen for basic_json template arguments
|
||||||
@@ -18655,20 +18677,6 @@ class binary_writer
|
|||||||
return 'D'; // float 64
|
return 'D'; // float 64
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief checks whether a JSON number fits into @a TargetType
|
|
||||||
@param[in] el a JSON number of either the signed or unsigned integer kind
|
|
||||||
@return whether @a el's value can be represented by @a TargetType without
|
|
||||||
wrapping, regardless of which of the two kinds it is stored as
|
|
||||||
*/
|
|
||||||
template<typename TargetType>
|
|
||||||
static bool bjdata_ndarray_value_in_range(const BasicJsonType& el)
|
|
||||||
{
|
|
||||||
return el.is_number_unsigned()
|
|
||||||
? value_in_range_of<TargetType>(el.template get<std::uint64_t>())
|
|
||||||
: value_in_range_of<TargetType>(el.template get<std::int64_t>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
|
@return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
|
||||||
*/
|
*/
|
||||||
@@ -18753,60 +18761,6 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// every element is cast to the (possibly narrower) C++ type matching
|
|
||||||
// dtype below; a value that does not fit that type would silently
|
|
||||||
// wrap (integers) or overflow to infinity (the "single" precision
|
|
||||||
// float) instead of being reported, so such an object falls back to
|
|
||||||
// a plain object encoding as well
|
|
||||||
for (const auto& el : value.at(key))
|
|
||||||
{
|
|
||||||
bool in_range = true;
|
|
||||||
switch (dtype)
|
|
||||||
{
|
|
||||||
case 'U':
|
|
||||||
case 'C':
|
|
||||||
case 'B':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint8_t>(el);
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int8_t>(el);
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint16_t>(el);
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int16_t>(el);
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint32_t>(el);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int32_t>(el);
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::uint64_t>(el);
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
in_range = bjdata_ndarray_value_in_range<std::int64_t>(el);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
{
|
|
||||||
const auto dval = el.template get<double>();
|
|
||||||
in_range = !std::isfinite(dval) ||
|
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// 'D' (double) already spans the full range of number_float_t
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!in_range)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
oa->write_character('[');
|
oa->write_character('[');
|
||||||
oa->write_character('$');
|
oa->write_character('$');
|
||||||
oa->write_character(dtype);
|
oa->write_character(dtype);
|
||||||
|
|||||||
@@ -2776,53 +2776,6 @@ TEST_CASE("BJData")
|
|||||||
CHECK(out_num.at(0) == '{');
|
CHECK(out_num.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_num) == j_num);
|
CHECK(json::from_bjdata(out_num) == j_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ndarray with out-of-range _ArrayData_ elements stays as object")
|
|
||||||
{
|
|
||||||
// each element is cast to the (possibly narrower) C++ type
|
|
||||||
// named by _ArrayType_ before being written; a value that
|
|
||||||
// does not fit that type would silently wrap instead of
|
|
||||||
// being reported, so such an object falls back to a plain
|
|
||||||
// object encoding that still round-trips (see GitHub issue #5403)
|
|
||||||
|
|
||||||
// an unsigned element that does not fit uint8
|
|
||||||
json const j_uint8 = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, 256}}});
|
|
||||||
const auto out_uint8 = json::to_bjdata(j_uint8);
|
|
||||||
CHECK(out_uint8.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_uint8) == j_uint8);
|
|
||||||
|
|
||||||
// a signed element that does not fit int8
|
|
||||||
json const j_int8 = json({{"_ArrayType_", "int8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, 200}}});
|
|
||||||
const auto out_int8 = json::to_bjdata(j_int8);
|
|
||||||
CHECK(out_int8.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_int8) == j_int8);
|
|
||||||
|
|
||||||
// a negative element is likewise out of range for an
|
|
||||||
// unsigned _ArrayType_
|
|
||||||
json const j_uint16_neg = json({{"_ArrayType_", "uint16"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, -1}}});
|
|
||||||
const auto out_uint16_neg = json::to_bjdata(j_uint16_neg);
|
|
||||||
CHECK(out_uint16_neg.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_uint16_neg) == j_uint16_neg);
|
|
||||||
|
|
||||||
// a double element that overflows to infinity when narrowed
|
|
||||||
// to the "single" (float) precision named by _ArrayType_
|
|
||||||
json const j_single = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1.5, 1e40}}});
|
|
||||||
const auto out_single = json::to_bjdata(j_single);
|
|
||||||
CHECK(out_single.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_single) == j_single);
|
|
||||||
|
|
||||||
// in-range boundary values still use the compact ndarray encoding
|
|
||||||
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {0, 255}}});
|
|
||||||
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, ']', 0, 255}));
|
|
||||||
|
|
||||||
json const j_int8_ok = json({{"_ArrayType_", "int8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {-128, 127}}});
|
|
||||||
CHECK(json::to_bjdata(j_int8_ok) == std::vector<uint8_t>({'[', '$', 'i', '#', '[', 'i', 2, ']', 0x80, 0x7F}));
|
|
||||||
|
|
||||||
json const j_single_ok = json({{"_ArrayType_", "single"}, {"_ArraySize_", {1}}, {"_ArrayData_", {1.5}}});
|
|
||||||
const auto out_single_ok = json::to_bjdata(j_single_ok);
|
|
||||||
CHECK(out_single_ok.at(0) == '[');
|
|
||||||
CHECK(json::from_bjdata(out_single_ok) == json({1.5f}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1389,6 +1389,37 @@ TEST_CASE("value conversion")
|
|||||||
// CHECK(m5["one"] == "eins");
|
// CHECK(m5["one"] == "eins");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("reserve is called on containers that support it (#5406)")
|
||||||
|
{
|
||||||
|
// build a larger object so that a missing/incorrect reserve()
|
||||||
|
// call would be more likely to corrupt or drop elements
|
||||||
|
json j_large;
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
j_large[std::to_string(i)] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::unordered_map (supports reserve)")
|
||||||
|
{
|
||||||
|
const auto m = j_large.get<std::unordered_map<std::string, int>>();
|
||||||
|
CHECK(m.size() == 100);
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
CHECK(m.at(std::to_string(i)) == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::map (no reserve, fallback path)")
|
||||||
|
{
|
||||||
|
const auto m = j_large.get<std::map<std::string, int>>();
|
||||||
|
CHECK(m.size() == 100);
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
CHECK(m.at(std::to_string(i)) == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("std::multimap")
|
SECTION("std::multimap")
|
||||||
{
|
{
|
||||||
j1.get<std::multimap<std::string, int>>();
|
j1.get<std::multimap<std::string, int>>();
|
||||||
|
|||||||
Reference in New Issue
Block a user