mirror of
https://github.com/nlohmann/json.git
synced 2026-10-09 07:57:13 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82e7ee29df |
No files matched your search
@@ -64,18 +64,7 @@ values of that type directly to a `basic_json` instance, and they will automatic
|
||||
rather than arrays:
|
||||
|
||||
```cpp
|
||||
using custom_json = nlohmann::basic_json<
|
||||
nlohmann::ordered_map, // ObjectType
|
||||
std::vector, // ArrayType
|
||||
std::string, // StringType
|
||||
bool, // BooleanType
|
||||
std::int64_t, // NumberIntegerType
|
||||
std::uint64_t, // NumberUnsignedType
|
||||
double, // NumberFloatType
|
||||
std::allocator, // AllocatorType
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::byte> // Custom BinaryType
|
||||
>;
|
||||
using custom_json = nlohmann::ordered_json::with_binary_t<std::vector<std::byte>>;
|
||||
|
||||
std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}};
|
||||
custom_json j = data; // Creates a binary value, not an array
|
||||
|
||||
@@ -15,19 +15,7 @@ class base_class_with_hidden_members
|
||||
}
|
||||
};
|
||||
|
||||
using json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
base_class_with_hidden_members
|
||||
>;
|
||||
using json = nlohmann::json::with_base_class_t<base_class_with_hidden_members>;
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include <iosfwd> // ostream
|
||||
#endif // JSON_NO_IO
|
||||
#include <limits> // max
|
||||
#include <map> // map
|
||||
#include <numeric> // accumulate
|
||||
#include <set> // set
|
||||
#include <string> // string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
@@ -359,45 +359,33 @@ class json_pointer
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief the pointer prefixes of a flattened object, and which of them denote arrays
|
||||
@brief the reference token sequences that denote arrays
|
||||
|
||||
@ref unflatten collects the pointer prefixes that have a reference token 0
|
||||
among their children; @ref get_and_create creates arrays exactly below
|
||||
those prefixes and objects everywhere else. Deciding this up front keeps
|
||||
the result independent of the order in which the flattened object is
|
||||
iterated, which is unspecified for some object types.
|
||||
|
||||
The prefixes form a tree and are numbered, so each of them is stored only
|
||||
once (as a node) rather than as a copy of all of its reference tokens.
|
||||
*/
|
||||
struct prefix_tree
|
||||
{
|
||||
// children[id] maps a reference token to the number of the prefix
|
||||
// extended by that token; number 0 is the empty prefix
|
||||
std::vector<std::map<string_t, std::size_t>> children;
|
||||
// is_array[id] is true iff some flattened key has the reference token
|
||||
// 0 directly below the prefix with number id
|
||||
std::vector<bool> is_array;
|
||||
};
|
||||
using array_parents_t = std::set<std::vector<string_t>>;
|
||||
|
||||
/*!
|
||||
@brief create and return a reference to the pointed to value
|
||||
|
||||
Complexity: Linear in the number of reference tokens (times the logarithm
|
||||
of the number of siblings for the prefix lookup).
|
||||
Complexity: Linear in the number of reference tokens.
|
||||
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if array index is not a number
|
||||
@throw type_error.313 if value cannot be unflattened
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
BasicJsonType& get_and_create(BasicJsonType& j, const prefix_tree& tree) const
|
||||
BasicJsonType& get_and_create(BasicJsonType& j, const array_parents_t& array_parents) const
|
||||
{
|
||||
auto* result = &j;
|
||||
|
||||
// the number of the prefix consumed so far; used to look up whether
|
||||
// the value to be created below is an array or an object
|
||||
std::size_t id = 0;
|
||||
// the reference tokens that have been consumed so far; used to look up
|
||||
// whether the value to be created below is an array or an object
|
||||
std::vector<string_t> prefix;
|
||||
|
||||
// in case no reference tokens exist, return a reference to the JSON value
|
||||
// j which will be overwritten by a primitive value
|
||||
@@ -407,7 +395,7 @@ class json_pointer
|
||||
{
|
||||
case detail::value_t::null:
|
||||
{
|
||||
if (tree.is_array[id])
|
||||
if (array_parents.find(prefix) != array_parents.end())
|
||||
{
|
||||
// some reference token below this position is 0, so the
|
||||
// value is an array
|
||||
@@ -452,9 +440,7 @@ class json_pointer
|
||||
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
|
||||
}
|
||||
|
||||
const auto it = tree.children[id].find(reference_token);
|
||||
JSON_ASSERT(it != tree.children[id].end());
|
||||
id = it->second;
|
||||
prefix.push_back(reference_token);
|
||||
}
|
||||
|
||||
return *result;
|
||||
@@ -892,127 +878,64 @@ class json_pointer
|
||||
@param[in,out] result the result object to insert values to
|
||||
|
||||
@note Empty objects or arrays are flattened to `null`.
|
||||
|
||||
The value is walked with an explicit stack rather than the call stack, so
|
||||
arbitrarily deeply nested values can be flattened.
|
||||
|
||||
@sa https://github.com/nlohmann/json/issues/5393
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static void flatten(const string_t& reference_string,
|
||||
const BasicJsonType& value,
|
||||
BasicJsonType& result)
|
||||
{
|
||||
using object_const_iterator = typename BasicJsonType::object_t::const_iterator;
|
||||
|
||||
// an array or object being walked: the container, the array index or
|
||||
// object iterator of the next child, and the length of the path of the
|
||||
// container itself
|
||||
struct frame
|
||||
switch (value.type())
|
||||
{
|
||||
const BasicJsonType* container;
|
||||
std::size_t index;
|
||||
object_const_iterator member;
|
||||
std::size_t path_length;
|
||||
};
|
||||
|
||||
// The containers being flattened are kept on an explicit stack, and
|
||||
// every child is flattened completely before the next one, so the
|
||||
// entries come out in the same order as with a recursive walk. The
|
||||
// path of the value being flattened is kept in one buffer that grows
|
||||
// and shrinks with the stack, rather than in a new string per level.
|
||||
std::vector<frame> stack;
|
||||
string_t path = reference_string;
|
||||
|
||||
// flatten `v`, whose path is `path`: primitives and empty containers
|
||||
// are added to the result right away; other containers get a frame
|
||||
const auto enter = [&stack, &path, &result](const BasicJsonType & v)
|
||||
{
|
||||
switch (v.type())
|
||||
case detail::value_t::array:
|
||||
{
|
||||
case detail::value_t::array:
|
||||
if (value.m_data.m_value.array->empty())
|
||||
{
|
||||
if (v.m_data.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
result[path] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push_back({&v, 0, object_const_iterator(), path.size()});
|
||||
}
|
||||
return;
|
||||
// flatten empty array as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
|
||||
case detail::value_t::object:
|
||||
else
|
||||
{
|
||||
if (v.m_data.m_value.object->empty())
|
||||
// iterate array and use index as a reference string
|
||||
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
|
||||
{
|
||||
// flatten empty object as null
|
||||
result[path] = nullptr;
|
||||
flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
|
||||
value.m_data.m_value.array->operator[](i), result);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push_back({&v, 0, v.m_data.m_value.object->begin(), path.size()});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
result[path] = v;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
enter(value);
|
||||
while (!stack.empty())
|
||||
{
|
||||
// the frame is changed through stack.back(): enter() may push a
|
||||
// frame, which would invalidate a reference to it
|
||||
const BasicJsonType* const container = stack.back().container;
|
||||
|
||||
// drop the path of the previous child
|
||||
path.resize(stack.back().path_length);
|
||||
|
||||
if (container->is_array())
|
||||
case detail::value_t::object:
|
||||
{
|
||||
const auto& array = *container->m_data.m_value.array;
|
||||
const std::size_t i = stack.back().index;
|
||||
if (i == array.size())
|
||||
if (value.m_data.m_value.object->empty())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
// flatten empty object as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
|
||||
// iterate array and use index as a reference string
|
||||
++stack.back().index;
|
||||
detail::concat_into(path, '/', detail::to_string<string_t>(i));
|
||||
enter(array[i]);
|
||||
else
|
||||
{
|
||||
// iterate object and use keys as reference string
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const object_const_iterator it = stack.back().member;
|
||||
if (it == container->m_data.m_value.object->end())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// iterate object and use keys as reference string
|
||||
++stack.back().member;
|
||||
detail::concat_into(path, '/', detail::escape(it->first));
|
||||
enter(it->second);
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
result[reference_string] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1040,31 +963,19 @@ class json_pointer
|
||||
|
||||
// collect the pointer prefixes that have a reference token 0 among
|
||||
// their children; the values below them are arrays, all others are
|
||||
// objects (see prefix_tree)
|
||||
prefix_tree tree;
|
||||
tree.children.emplace_back();
|
||||
tree.is_array.push_back(false);
|
||||
// objects (see array_parents_t)
|
||||
array_parents_t array_parents;
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
json_pointer ptr(element.first);
|
||||
std::size_t id = 0;
|
||||
std::vector<string_t> prefix;
|
||||
for (auto& reference_token : ptr.reference_tokens)
|
||||
{
|
||||
if (reference_token == "0")
|
||||
{
|
||||
tree.is_array[id] = true;
|
||||
}
|
||||
|
||||
// do not keep a reference into tree.children across the
|
||||
// push_back below, as it may reallocate
|
||||
const std::size_t next = tree.children.size();
|
||||
const auto inserted = tree.children[id].emplace(std::move(reference_token), next);
|
||||
id = inserted.first->second;
|
||||
if (inserted.second)
|
||||
{
|
||||
tree.children.emplace_back();
|
||||
tree.is_array.push_back(false);
|
||||
array_parents.insert(prefix);
|
||||
}
|
||||
prefix.push_back(std::move(reference_token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1080,7 +991,7 @@ class json_pointer
|
||||
// that if the JSON pointer is "" (i.e., points to the whole value),
|
||||
// function get_and_create returns a reference to the result itself.
|
||||
// An assignment will then create a primitive value.
|
||||
json_pointer(element.first).get_and_create(result, tree) = element.second;
|
||||
json_pointer(element.first).get_and_create(result, array_parents) = element.second;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -19928,8 +19928,8 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
#include <iosfwd> // ostream
|
||||
#endif // JSON_NO_IO
|
||||
#include <limits> // max
|
||||
#include <map> // map
|
||||
#include <numeric> // accumulate
|
||||
#include <set> // set
|
||||
#include <string> // string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
@@ -20277,45 +20277,33 @@ class json_pointer
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief the pointer prefixes of a flattened object, and which of them denote arrays
|
||||
@brief the reference token sequences that denote arrays
|
||||
|
||||
@ref unflatten collects the pointer prefixes that have a reference token 0
|
||||
among their children; @ref get_and_create creates arrays exactly below
|
||||
those prefixes and objects everywhere else. Deciding this up front keeps
|
||||
the result independent of the order in which the flattened object is
|
||||
iterated, which is unspecified for some object types.
|
||||
|
||||
The prefixes form a tree and are numbered, so each of them is stored only
|
||||
once (as a node) rather than as a copy of all of its reference tokens.
|
||||
*/
|
||||
struct prefix_tree
|
||||
{
|
||||
// children[id] maps a reference token to the number of the prefix
|
||||
// extended by that token; number 0 is the empty prefix
|
||||
std::vector<std::map<string_t, std::size_t>> children;
|
||||
// is_array[id] is true iff some flattened key has the reference token
|
||||
// 0 directly below the prefix with number id
|
||||
std::vector<bool> is_array;
|
||||
};
|
||||
using array_parents_t = std::set<std::vector<string_t>>;
|
||||
|
||||
/*!
|
||||
@brief create and return a reference to the pointed to value
|
||||
|
||||
Complexity: Linear in the number of reference tokens (times the logarithm
|
||||
of the number of siblings for the prefix lookup).
|
||||
Complexity: Linear in the number of reference tokens.
|
||||
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if array index is not a number
|
||||
@throw type_error.313 if value cannot be unflattened
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
BasicJsonType& get_and_create(BasicJsonType& j, const prefix_tree& tree) const
|
||||
BasicJsonType& get_and_create(BasicJsonType& j, const array_parents_t& array_parents) const
|
||||
{
|
||||
auto* result = &j;
|
||||
|
||||
// the number of the prefix consumed so far; used to look up whether
|
||||
// the value to be created below is an array or an object
|
||||
std::size_t id = 0;
|
||||
// the reference tokens that have been consumed so far; used to look up
|
||||
// whether the value to be created below is an array or an object
|
||||
std::vector<string_t> prefix;
|
||||
|
||||
// in case no reference tokens exist, return a reference to the JSON value
|
||||
// j which will be overwritten by a primitive value
|
||||
@@ -20325,7 +20313,7 @@ class json_pointer
|
||||
{
|
||||
case detail::value_t::null:
|
||||
{
|
||||
if (tree.is_array[id])
|
||||
if (array_parents.find(prefix) != array_parents.end())
|
||||
{
|
||||
// some reference token below this position is 0, so the
|
||||
// value is an array
|
||||
@@ -20370,9 +20358,7 @@ class json_pointer
|
||||
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
|
||||
}
|
||||
|
||||
const auto it = tree.children[id].find(reference_token);
|
||||
JSON_ASSERT(it != tree.children[id].end());
|
||||
id = it->second;
|
||||
prefix.push_back(reference_token);
|
||||
}
|
||||
|
||||
return *result;
|
||||
@@ -20810,127 +20796,64 @@ class json_pointer
|
||||
@param[in,out] result the result object to insert values to
|
||||
|
||||
@note Empty objects or arrays are flattened to `null`.
|
||||
|
||||
The value is walked with an explicit stack rather than the call stack, so
|
||||
arbitrarily deeply nested values can be flattened.
|
||||
|
||||
@sa https://github.com/nlohmann/json/issues/5393
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static void flatten(const string_t& reference_string,
|
||||
const BasicJsonType& value,
|
||||
BasicJsonType& result)
|
||||
{
|
||||
using object_const_iterator = typename BasicJsonType::object_t::const_iterator;
|
||||
|
||||
// an array or object being walked: the container, the array index or
|
||||
// object iterator of the next child, and the length of the path of the
|
||||
// container itself
|
||||
struct frame
|
||||
switch (value.type())
|
||||
{
|
||||
const BasicJsonType* container;
|
||||
std::size_t index;
|
||||
object_const_iterator member;
|
||||
std::size_t path_length;
|
||||
};
|
||||
|
||||
// The containers being flattened are kept on an explicit stack, and
|
||||
// every child is flattened completely before the next one, so the
|
||||
// entries come out in the same order as with a recursive walk. The
|
||||
// path of the value being flattened is kept in one buffer that grows
|
||||
// and shrinks with the stack, rather than in a new string per level.
|
||||
std::vector<frame> stack;
|
||||
string_t path = reference_string;
|
||||
|
||||
// flatten `v`, whose path is `path`: primitives and empty containers
|
||||
// are added to the result right away; other containers get a frame
|
||||
const auto enter = [&stack, &path, &result](const BasicJsonType & v)
|
||||
{
|
||||
switch (v.type())
|
||||
case detail::value_t::array:
|
||||
{
|
||||
case detail::value_t::array:
|
||||
if (value.m_data.m_value.array->empty())
|
||||
{
|
||||
if (v.m_data.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
result[path] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push_back({&v, 0, object_const_iterator(), path.size()});
|
||||
}
|
||||
return;
|
||||
// flatten empty array as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
|
||||
case detail::value_t::object:
|
||||
else
|
||||
{
|
||||
if (v.m_data.m_value.object->empty())
|
||||
// iterate array and use index as a reference string
|
||||
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
|
||||
{
|
||||
// flatten empty object as null
|
||||
result[path] = nullptr;
|
||||
flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
|
||||
value.m_data.m_value.array->operator[](i), result);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push_back({&v, 0, v.m_data.m_value.object->begin(), path.size()});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
result[path] = v;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
enter(value);
|
||||
while (!stack.empty())
|
||||
{
|
||||
// the frame is changed through stack.back(): enter() may push a
|
||||
// frame, which would invalidate a reference to it
|
||||
const BasicJsonType* const container = stack.back().container;
|
||||
|
||||
// drop the path of the previous child
|
||||
path.resize(stack.back().path_length);
|
||||
|
||||
if (container->is_array())
|
||||
case detail::value_t::object:
|
||||
{
|
||||
const auto& array = *container->m_data.m_value.array;
|
||||
const std::size_t i = stack.back().index;
|
||||
if (i == array.size())
|
||||
if (value.m_data.m_value.object->empty())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
// flatten empty object as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
|
||||
// iterate array and use index as a reference string
|
||||
++stack.back().index;
|
||||
detail::concat_into(path, '/', detail::to_string<string_t>(i));
|
||||
enter(array[i]);
|
||||
else
|
||||
{
|
||||
// iterate object and use keys as reference string
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const object_const_iterator it = stack.back().member;
|
||||
if (it == container->m_data.m_value.object->end())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// iterate object and use keys as reference string
|
||||
++stack.back().member;
|
||||
detail::concat_into(path, '/', detail::escape(it->first));
|
||||
enter(it->second);
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
result[reference_string] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20958,31 +20881,19 @@ class json_pointer
|
||||
|
||||
// collect the pointer prefixes that have a reference token 0 among
|
||||
// their children; the values below them are arrays, all others are
|
||||
// objects (see prefix_tree)
|
||||
prefix_tree tree;
|
||||
tree.children.emplace_back();
|
||||
tree.is_array.push_back(false);
|
||||
// objects (see array_parents_t)
|
||||
array_parents_t array_parents;
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
json_pointer ptr(element.first);
|
||||
std::size_t id = 0;
|
||||
std::vector<string_t> prefix;
|
||||
for (auto& reference_token : ptr.reference_tokens)
|
||||
{
|
||||
if (reference_token == "0")
|
||||
{
|
||||
tree.is_array[id] = true;
|
||||
}
|
||||
|
||||
// do not keep a reference into tree.children across the
|
||||
// push_back below, as it may reallocate
|
||||
const std::size_t next = tree.children.size();
|
||||
const auto inserted = tree.children[id].emplace(std::move(reference_token), next);
|
||||
id = inserted.first->second;
|
||||
if (inserted.second)
|
||||
{
|
||||
tree.children.emplace_back();
|
||||
tree.is_array.push_back(false);
|
||||
array_parents.insert(prefix);
|
||||
}
|
||||
prefix.push_back(std::move(reference_token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20998,7 +20909,7 @@ class json_pointer
|
||||
// that if the JSON pointer is "" (i.e., points to the whole value),
|
||||
// function get_and_create returns a reference to the result itself.
|
||||
// An assignment will then create a primitive value.
|
||||
json_pointer(element.first).get_and_create(result, tree) = element.second;
|
||||
json_pointer(element.first).get_and_create(result, array_parents) = element.second;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -46,9 +46,7 @@ class huge_binary_t : public std::vector<std::uint8_t>
|
||||
}
|
||||
};
|
||||
|
||||
using huge_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, huge_binary_t, void >;
|
||||
using huge_binary_json = nlohmann::json::with_binary_t<huge_binary_t>;
|
||||
|
||||
// a string type that can be made to report a size beyond INT32_MAX without
|
||||
// allocating that much memory, so BSON length overflow can be tested for
|
||||
@@ -96,9 +94,7 @@ class huge_string_t : public std::string
|
||||
bool pretend_huge = false;
|
||||
};
|
||||
|
||||
using huge_string_json = nlohmann::basic_json <
|
||||
std::map, std::vector, huge_string_t, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<std::uint8_t>, void >;
|
||||
using huge_string_json = nlohmann::json::with_string_t<huge_string_t>;
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("BSON")
|
||||
|
||||
@@ -400,20 +400,7 @@ class base_class_with_hidden_members
|
||||
std::size_t m_size = 42;
|
||||
};
|
||||
|
||||
using json_with_hidden_base_members =
|
||||
nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
base_class_with_hidden_members
|
||||
>;
|
||||
using json_with_hidden_base_members = nlohmann::json::with_base_class_t<base_class_with_hidden_members>;
|
||||
|
||||
TEST_CASE("JSON Node as_base_class")
|
||||
{
|
||||
@@ -459,19 +446,7 @@ struct const_member_base
|
||||
const int id = 7; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
};
|
||||
|
||||
using json_with_const_base = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
const_member_base
|
||||
>;
|
||||
using json_with_const_base = nlohmann::json::with_base_class_t<const_member_base>;
|
||||
|
||||
// build an array nested @a depth levels deep, with the innermost value 1;
|
||||
// every level is constructed (never assigned), since const_member_base does
|
||||
|
||||
@@ -26,15 +26,11 @@ namespace
|
||||
|
||||
// a BinaryType whose value type is signed: the elements must still be
|
||||
// processed as the numbers 0..255
|
||||
using char_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<char>, void >;
|
||||
using char_binary_json = nlohmann::json::with_binary_t<std::vector<char>>;
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
// a BinaryType whose value type is not an integer type at all
|
||||
using byte_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<std::byte>, void >;
|
||||
using byte_binary_json = nlohmann::json::with_binary_t<std::vector<std::byte>>;
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -939,114 +939,3 @@ TEST_CASE("unescaping keeps a '~' that does not start an escape sequence")
|
||||
nlohmann::detail::unescape(s);
|
||||
CHECK(s == "~/~");
|
||||
}
|
||||
|
||||
TEST_CASE("flatten of structured values")
|
||||
{
|
||||
SECTION("values nested too deeply for the call stack (#5393)")
|
||||
{
|
||||
// flatten() used to recurse once per nesting level
|
||||
const std::size_t depth = 100000;
|
||||
for (const bool objects :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CAPTURE(objects)
|
||||
std::string text;
|
||||
std::string path;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
text += objects ? "{\"a\":" : "[";
|
||||
path += objects ? "/a" : "/0";
|
||||
}
|
||||
text += "0";
|
||||
text += std::string(depth, objects ? '}' : ']');
|
||||
const auto value = json::parse(text);
|
||||
|
||||
const auto flat = value.flatten();
|
||||
REQUIRE(flat.size() == 1);
|
||||
REQUIRE(flat.begin().key().size() == path.size());
|
||||
CHECK(flat.begin().key() == path);
|
||||
CHECK(flat.begin().value() == 0);
|
||||
|
||||
// unflatten() is linear in the depth, so the value roundtrips
|
||||
CHECK(flat.unflatten() == value);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unflatten of a deeply nested pointer")
|
||||
{
|
||||
const std::size_t depth = 100000;
|
||||
for (const bool objects :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CAPTURE(objects)
|
||||
std::string path;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
path += objects ? "/a" : "/0";
|
||||
}
|
||||
|
||||
json flat = json::object();
|
||||
flat[path] = 1;
|
||||
const json value = flat.unflatten();
|
||||
|
||||
// walk down iteratively
|
||||
std::size_t levels = 0;
|
||||
const json* current = &value;
|
||||
while (objects ? current->is_object() : current->is_array())
|
||||
{
|
||||
REQUIRE(current->size() == 1);
|
||||
current = objects ? ¤t->at("a") : ¤t->at(0);
|
||||
++levels;
|
||||
}
|
||||
CHECK(levels == depth);
|
||||
CHECK(*current == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unflatten does not depend on the iteration order")
|
||||
{
|
||||
// the "0" key comes after its sibling in iteration order
|
||||
const nlohmann::ordered_json flat_array = nlohmann::ordered_json::parse(R"({"/a/1": 2, "/a/0": 1})");
|
||||
CHECK(flat_array.unflatten() == nlohmann::ordered_json::parse(R"({"a": [1, 2]})"));
|
||||
|
||||
const nlohmann::ordered_json flat_object = nlohmann::ordered_json::parse(R"({"/b/1": 2})");
|
||||
CHECK(flat_object.unflatten() == nlohmann::ordered_json::parse(R"({"b": {"1": 2}})"));
|
||||
}
|
||||
|
||||
SECTION("objects and arrays interleaved")
|
||||
{
|
||||
const json value =
|
||||
{
|
||||
{"a", {1, {{"b", json::array()}, {"c", json::object()}}, json::array({{{"x~/", {true, nullptr}}}})}},
|
||||
{"a/b", {{"~", 1}}},
|
||||
{"z", "s"}
|
||||
};
|
||||
|
||||
const json expected =
|
||||
{
|
||||
{"/a/0", 1},
|
||||
{"/a/1/b", nullptr},
|
||||
{"/a/1/c", nullptr},
|
||||
{"/a/2/0/x~0~1/0", true},
|
||||
{"/a/2/0/x~0~1/1", nullptr},
|
||||
{"/a~1b/~0", 1},
|
||||
{"/z", "s"}
|
||||
};
|
||||
|
||||
CHECK(value.flatten() == expected);
|
||||
}
|
||||
|
||||
SECTION("order of the entries of an ordered_json")
|
||||
{
|
||||
const auto value = nlohmann::ordered_json::parse(
|
||||
R"({"z":"s","a/b":{"~":1,"k":[]},"a":[1,{"c":{},"b":[]},[{"x~/":[true,null],"w":2}]]})");
|
||||
|
||||
const auto flat = value.flatten();
|
||||
CHECK(flat.dump() ==
|
||||
R"({"/z":"s","/a~1b/~0":1,"/a~1b/k":null,"/a/0":1,"/a/1/c":null,"/a/1/b":null,"/a/2/0/x~0~1/0":true,"/a/2/0/x~0~1/1":null,"/a/2/0/w":2})");
|
||||
}
|
||||
}
|
||||
@@ -2201,10 +2201,7 @@ struct huge_array : std::vector<T, A>
|
||||
}
|
||||
};
|
||||
|
||||
using huge_array_json = nlohmann::basic_json <
|
||||
std::map, huge_array, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>, void >;
|
||||
using huge_array_json = nlohmann::json::with_array_t<huge_array>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for array")
|
||||
{
|
||||
@@ -2249,18 +2246,7 @@ template<typename K, typename V,
|
||||
}
|
||||
};
|
||||
|
||||
using huge_object_json = nlohmann::basic_json <
|
||||
huge_map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
void >;
|
||||
using huge_object_json = nlohmann::json::with_object_t<huge_map>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for object")
|
||||
{
|
||||
@@ -2295,18 +2281,7 @@ struct huge_string : std::string
|
||||
}
|
||||
};
|
||||
|
||||
using huge_string_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
huge_string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
void >;
|
||||
using huge_string_json = nlohmann::json::with_string_t<huge_string>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for string")
|
||||
{
|
||||
@@ -2329,18 +2304,7 @@ struct huge_binary : std::vector<std::uint8_t>
|
||||
}
|
||||
};
|
||||
|
||||
using huge_binary_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
huge_binary,
|
||||
void >;
|
||||
using huge_binary_json = nlohmann::json::with_binary_t<huge_binary>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for binary")
|
||||
{
|
||||
@@ -2390,14 +2354,10 @@ class beyond_uint32_string_t : public std::string
|
||||
}
|
||||
};
|
||||
|
||||
using beyond_uint32_string_json = nlohmann::basic_json <
|
||||
std::map, std::vector, beyond_uint32_string_t, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<std::uint8_t>, void >;
|
||||
using beyond_uint32_string_json = nlohmann::json::with_string_t<beyond_uint32_string_t>;
|
||||
#endif
|
||||
|
||||
using beyond_uint32_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, beyond_uint32_binary_t, void >;
|
||||
using beyond_uint32_binary_json = nlohmann::json::with_binary_t<beyond_uint32_binary_t>;
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("MessagePack lengths beyond UINT32_MAX cannot be serialized")
|
||||
|
||||
@@ -217,16 +217,7 @@ void int_to_string(alt_string& target, std::size_t value)
|
||||
target = std::to_string(value).c_str();
|
||||
}
|
||||
|
||||
using alt_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
alt_string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer >;
|
||||
using alt_json = nlohmann::json::with_string_t<alt_string>;
|
||||
|
||||
bool operator<(const char* op1, const alt_string& op2) noexcept
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user