diff --git a/docs/mkdocs/docs/api/basic_json_view/get.md b/docs/mkdocs/docs/api/basic_json_view/get.md index 7fe30ca7f..40fd1033a 100644 --- a/docs/mkdocs/docs/api/basic_json_view/get.md +++ b/docs/mkdocs/docs/api/basic_json_view/get.md @@ -27,8 +27,25 @@ For the types below, the conversion works directly on the flat index -- no `Basi [`materialize()`](materialize.md)) does; `#!cpp std::map` keeps views of the members instead of values -Every other `T` -- `#!cpp std::list`, `#!cpp std::pair`, `#!cpp std::array`, enumerations, user types with a -`from_json()`, ... -- is converted by `#!cpp materialize().get()`: the subtree is built into a real `BasicJsonType` +A default-constructible `T` whose `from_json()` accepts views reads the view directly as well, member by member, without +building a `BasicJsonType` value. This covers the types of the +[`NLOHMANN_DEFINE_TYPE_*`](../macros/nlohmann_define_type_intrusive.md) and +[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../macros/nlohmann_define_derived_type.md) macros and the enumerations of +[`NLOHMANN_JSON_SERIALIZE_ENUM`](../macros/nlohmann_json_serialize_enum.md), and any `from_json()` constrained to +accept views, e.g. with [`is_json_view`](../is_json_view.md). A `from_json()` that also accepts types that are no JSON +values at all, such as the unconstrained `#!cpp template void from_json(const BasicJsonType&, +T&)` of [arbitrary type conversions](../../features/arbitrary_types.md), is not called with a view: it may use +`BasicJsonType` functions a view does not have. See [json_view](../../features/json_view.md). + +!!! warning "Duplicate keys" + + Such a `from_json()` looks members up in the view, where a repeated key finds the **first** member, while + `#!cpp materialize().get()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md) + keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get()` reads `#!cpp x == 1`. See + [Duplicate keys](../../features/json_view.md#duplicate-keys). + +Every other `T` -- `#!cpp std::list`, `#!cpp std::pair`, `#!cpp std::array`, user types whose `from_json()` is +written for `BasicJsonType` only, ... -- is converted by `#!cpp materialize().get()`: the subtree is built into a real `BasicJsonType` value first (as [`BasicJsonType::parse()`](../basic_json/parse.md) would), and converted from there exactly as [`BasicJsonType::get()`](../basic_json/get.md) would convert it. @@ -55,10 +72,13 @@ Strong guarantee: there are no changes to the view or the document it refers to. an element to `U` throws. - For `#!cpp std::map`/`#!cpp std::unordered_map`: throws `type_error.302` if the value is not an object; otherwise, whatever converting a member to the mapped type throws. +- For a `T` whose `from_json()` reads the view: whatever that `from_json()` throws; for the macros, the same + exceptions with the same messages as [`BasicJsonType::get()`](../basic_json/get.md) of the same value. - For every other `T`: whatever [`materialize().get()`](../basic_json/get.md) throws -- typically `type_error.302`, or whatever a user-provided `from_json()` throws. -None of the exceptions thrown directly by this function (the first three bullets above) carry a +None of the exceptions thrown directly by this function or by a `from_json()` that reads the view (the first four +bullets above) carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType` value to point at. An exception thrown while converting through `materialize()` (the last bullet) is different: it is thrown by a real `BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled. @@ -76,6 +96,8 @@ document. - `#!cpp std::map`/`#!cpp std::unordered_map`: linear in the number of members for walking them, plus the container's own insertion cost per member (logarithmic for `#!cpp std::map`, amortized constant for `#!cpp std::unordered_map`), times the complexity of converting one member to the mapped type. +- a `T` whose `from_json()` reads the view: the cost of that `from_json()`; for the macros, linear in the number of + members times the cost of a lookup, plus the cost of converting the members. - every other `T`: the cost of [`materialize()`](materialize.md) and of destroying the `BasicJsonType` value afterwards, plus that of [`BasicJsonType::get()`](../basic_json/get.md). diff --git a/docs/mkdocs/docs/api/basic_json_view/get_to.md b/docs/mkdocs/docs/api/basic_json_view/get_to.md index 4f1c6a5d9..4d2dd2fa5 100644 --- a/docs/mkdocs/docs/api/basic_json_view/get_to.md +++ b/docs/mkdocs/docs/api/basic_json_view/get_to.md @@ -5,12 +5,14 @@ template T& get_to(T& v) const; ``` -Converts the value to `T` and stores it in `v`, in one of two ways: +Converts the value to `T` and stores it in `v`, in one of three ways: - For the types that [`get()`](get.md) converts directly -- arithmetic types, `#!cpp bool`, `#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `#!cpp std::basic_string`, `BasicJsonType`, `basic_json_view`, `#!cpp std::vector`, and `#!cpp std::map`/`#!cpp std::unordered_map` with string keys -- `v` is assigned: `#!cpp v = get()`. +- For a type whose `from_json()` accepts views (see [`get()`](get.md)), that `from_json()` is called with the + view and the existing `v`, without building a `BasicJsonType` value. - For every other type, the subtree is built into a `BasicJsonType` value and `#!cpp materialize().get_to(v)` is called, i.e. `from_json()` is invoked with the existing `v`, exactly as [`BasicJsonType::get_to()`](../basic_json/get_to.md) does. diff --git a/docs/mkdocs/docs/api/basic_json_view/index.md b/docs/mkdocs/docs/api/basic_json_view/index.md index 5049e2242..4a634b8f0 100644 --- a/docs/mkdocs/docs/api/basic_json_view/index.md +++ b/docs/mkdocs/docs/api/basic_json_view/index.md @@ -64,6 +64,7 @@ still refers to it -- including ones taken before the change -- reads the new va ## Member types +- **basic_json_type** - `BasicJsonType` (it also marks the class for [`is_json_view`](../is_json_view.md)) - **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md) - **string_t**, **number_integer_t**, **number_unsigned_t**, **number_float_t**, **json_pointer** - the corresponding member types of `BasicJsonType` diff --git a/docs/mkdocs/docs/api/is_json_view.md b/docs/mkdocs/docs/api/is_json_view.md new file mode 100644 index 000000000..53bd8ec3a --- /dev/null +++ b/docs/mkdocs/docs/api/is_json_view.md @@ -0,0 +1,55 @@ +# nlohmann::is_json_view + +Defined in header `` + +```cpp +template +struct is_json_view; +``` + +A type trait: `#!cpp is_json_view::value` is `#!cpp true` if `T` is a [`basic_json_view`](basic_json_view/index.md) +of any `BasicJsonType` and any [`view_mode`](view_mode.md) -- [`json_view`](json_view.md), +[`json_editable_view`](json_editable_view.md), [`ordered_json_view`](ordered_json_view.md), ... -- and +`#!cpp false` otherwise, for [`basic_json`](basic_json/index.md) as well. + +It is meant for constraining templates that accept views, in particular a `from_json()` that should read views +directly: [`get()`](basic_json_view/get.md) on a view calls a `from_json()` with the view only if that `from_json()` +accepts views but not arbitrary types. + +## Template parameters + +`T` +: the type to check (without cv-qualifiers or references) + +## Notes + +A view is recognized by its member type `basic_json_type` (a [`basic_json`](basic_json/index.md) specialization) and +its [`materialize()`](basic_json_view/materialize.md) returning that type, so the trait needs no declaration of the +view class. + +## Examples + +??? example + + The example below shows the trait, and a `from_json()` constrained to `json` values and views: `get()` on a view + reads the view directly. + + ```cpp + --8<-- "examples/is_json_view.cpp" + ``` + + Output: + + ``` + --8<-- "examples/is_json_view.output" + ``` + +## See also + +- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document +- [get](basic_json_view/get.md) - which conversions read the view directly +- [view_mode](view_mode.md) - read-only or editable + +## Version history + +Added in version 3.13.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md b/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md index c539e77a0..ef0a8c441 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md @@ -147,6 +147,13 @@ void to_json(BasicJsonType& j, const B& b) { See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md), respectively. +!!! info "Views" + + The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s + (``): [`get()`](../basic_json_view/get.md) on a view looks the members up in the + view, without building a `json` value first. The base part is read from the view as well if the base's + `from_json()` accepts views, and from [`materialize()`](../basic_json_view/materialize.md) otherwise. + ## Examples ??? example "Example: (1) NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE" @@ -192,3 +199,5 @@ void to_json(BasicJsonType& j, const B& b) { 6. Added in version 3.12.0. All six macros were changed to allow an empty member list in version 3.13.0. + +The generated `from_json()` reads the views of `json_document`s since version 3.13.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md index dab5c7070..b50022e05 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md @@ -81,6 +81,12 @@ See the examples below for the concrete generated code. that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by hand, building/reading a `json::array()` of the members in order. +!!! info "Views" + + The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s + (``): [`get()`](../basic_json_view/get.md) on a view looks the members up in the + view, without building a `json` value first. + ## Examples ??? example "Example: (1) NLOHMANN_DEFINE_TYPE_INTRUSIVE" @@ -189,3 +195,5 @@ See the examples below for the concrete generated code. All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0, and to allow an empty member list in version 3.13.0. + +The generated `from_json()` reads the views of `json_document`s since version 3.13.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md index 07d0166d0..373af300f 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md @@ -80,6 +80,12 @@ See the examples below for the concrete generated code. that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by hand, building/reading a `json::array()` of the members in order. +!!! info "Views" + + The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s + (``): [`get()`](../basic_json_view/get.md) on a view looks the members up in the + view, without building a `json` value first. + ## Examples ??? example "Example: (1) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE" @@ -188,3 +194,5 @@ See the examples below for the concrete generated code. All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0, and to allow an empty member list in version 3.13.0. + +The generated `from_json()` reads the views of `json_document`s since version 3.13.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md index 4af3b07b8..6382996ba 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md @@ -45,6 +45,12 @@ inline void from_json(const BasicJsonType& j, type& e); Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects with the converted keys. Such maps can be read from both forms. +!!! info "Views" + + The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s + (``), comparing the view with the entries of the mapping, without building a `json` value + first. + ## Examples ??? example "Example: Basic usage" @@ -88,3 +94,5 @@ inline void from_json(const BasicJsonType& j, type& e); ## Version history Added in version 3.4.0. + +The generated `from_json()` reads the views of `json_document`s since version 3.13.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md index f5806a490..d1513f4e7 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md @@ -48,6 +48,12 @@ inline void from_json(const BasicJsonType& j, type& e); Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects with the converted keys. Such maps can be read from both forms. +!!! info "Views" + + The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s + (``), comparing the view with the entries of the mapping, without building a `json` value + first. + ## Examples ??? example "Example: Basic usage" diff --git a/docs/mkdocs/docs/examples/is_json_view.cpp b/docs/mkdocs/docs/examples/is_json_view.cpp new file mode 100644 index 000000000..c7c34ee1a --- /dev/null +++ b/docs/mkdocs/docs/examples/is_json_view.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +using nlohmann::json; + +namespace ns +{ +struct person +{ + std::string name; + int age = 0; +}; + +// reads json values and views (of any document type), but nothing else +template < typename J, typename std::enable_if < nlohmann::is_json_view::value || std::is_same::value, int >::type = 0 > +void from_json(const J& j, person& p) +{ + p.name = j.at("name").template get(); + p.age = j.at("age").template get(); +} +} // namespace ns + +int main() +{ + std::cout << std::boolalpha + << nlohmann::is_json_view::value << ' ' + << nlohmann::is_json_view::value << ' ' + << nlohmann::is_json_view::value << '\n'; + + const nlohmann::json_document doc = nlohmann::json_document::parse(R"({"name": "Ada", "age": 36})"); + const auto p = doc.root().get(); // reads the view, no json value is built + std::cout << p.name << ' ' << p.age << '\n'; +} diff --git a/docs/mkdocs/docs/examples/is_json_view.output b/docs/mkdocs/docs/examples/is_json_view.output new file mode 100644 index 000000000..df2203a21 --- /dev/null +++ b/docs/mkdocs/docs/examples/is_json_view.output @@ -0,0 +1,2 @@ +true true false +Ada 36 diff --git a/docs/mkdocs/docs/features/json_view.md b/docs/mkdocs/docs/features/json_view.md index 89e26bdb3..7de4a07cd 100644 --- a/docs/mkdocs/docs/features/json_view.md +++ b/docs/mkdocs/docs/features/json_view.md @@ -185,6 +185,10 @@ key as well. Other zero-copy parsers that index the source text, such as yyjson member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees duplicates is unpredictable, so neither choice is wrong. +The same holds for [`get()`](../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such as +the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get()` of +`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get()` reads `#!cpp 2`. + **What stays the same as `parse()`?** [`materialize()`](../api/basic_json_view/materialize.md) and [`get>()`](../api/basic_json_view/get.md) replay every member in order, so they keep the *last* value exactly like `#!cpp basic_json::parse()` (at the position of the first occurrence of the key, for an diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index dcaf6b4af..59c0024d0 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -358,6 +358,7 @@ nav: - 'Overview': api/adl_serializer/index.md - 'from_json': api/adl_serializer/from_json.md - 'to_json': api/adl_serializer/to_json.md + - 'is_json_view': api/is_json_view.md - 'json': api/json.md - 'json_document': api/json_document.md - 'json_editable_document': api/json_editable_document.md diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 51f26706c..696afc34b 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -10,6 +10,7 @@ #include // min, transform #include // array +#include // nullptr_t #include // forward_list #include // inserter, front_inserter, end #include // map @@ -27,6 +28,7 @@ #include #include #include +#include #include #include @@ -665,16 +667,114 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p) } #endif -struct from_json_fn +namespace from_json_adl { - template - auto operator()(const BasicJsonType& j, T&& val) const +// hides the library's from_json() overloads above (and the from_json object): +// a call from this namespace finds only the from_json() functions of the +// arguments' own namespaces and classes (ADL); the declaration is never viable +// and never defined (not "= delete", which GCC before 6 reports as a use) +struct hidden; +void from_json(const hidden&); + +// a type no from_json() written for JSON values accepts: a from_json() that +// accepts it is not constrained to them (see has_view_from_json; it lives +// here, so that ADL finds no other from_json() for it) +struct probe +{}; + +struct fn +{ + template + auto operator()(const SourceType& j, T&& val) const noexcept(noexcept(from_json(j, std::forward(val)))) -> decltype(from_json(j, std::forward(val))) { return from_json(j, std::forward(val)); } }; +} // namespace from_json_adl + +// the sources whose from_json() is only looked up by ADL: views (the library's +// overloads are written for basic_json) and the probe +template +struct is_adl_only_from_json_source : disjunction, std::is_same> {}; + +struct from_json_fn +{ + template < typename BasicJsonType, typename T, + enable_if_t < !is_adl_only_from_json_source::value, int > = 0 > + auto operator()(const BasicJsonType& j, T && val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } + + template < typename SourceType, typename T, + enable_if_t < is_adl_only_from_json_source::value, int > = 0 > + auto operator()(const SourceType& j, T && val) const + noexcept(noexcept(from_json_adl::fn {}(j, std::forward(val)))) + -> decltype(from_json_adl::fn {}(j, std::forward(val))) + { + return from_json_adl::fn {}(j, std::forward(val)); + } +}; + +// Whether JSONSerializer::from_json(view, T&) reads T from a view (of +// ) directly: it must accept the view but not the +// probe. An unconstrained from_json() template (written for basic_json, but +// accepting anything) is thus not called with a view, nor are the library's +// own conversions; the view converts such types through materialize(). +template +using serializer_from_json_t = decltype(Serializer::from_json(std::declval(), std::declval())); + +template +struct has_view_from_json + : conjunction < is_detected, + negation>> {}; + +// the base part of a type of the NLOHMANN_DEFINE_DERIVED_TYPE_* macros: from a +// view through its get_to(), which reads the base from the view where the +// base's from_json() accepts views, and from materialize() otherwise +template::value, int> = 0> +void from_json_base(const BasicJsonType& j, T& base) +{ + from_json_fn {}(j, base); +} + +template::value, int> = 0> +void from_json_base(const View& v, T& base) +{ + v.get_to(base); +} + +// the context of an exception thrown by a from_json() of the macros: the value +// itself for basic_json (for JSON_DIAGNOSTICS), none for a view +template::value, int> = 0> +const BasicJsonType * from_json_context(const BasicJsonType& j) noexcept +{ + return &j; +} + +template::value, int> = 0> +std::nullptr_t from_json_context(const View& /*unused*/) noexcept +{ + return nullptr; +} + +// a value as an exception message shows it (invalid UTF-8 replaced; the +// strings of a view are always valid UTF-8) +template::value, int> = 0> +typename BasicJsonType::string_t from_json_dump(const BasicJsonType& j) +{ + return j.dump(-1, ' ', false, error_handler_t::replace); +} + +template::value, int> = 0> +typename View::string_t from_json_dump(const View& v) +{ + return v.dump(); +} } // namespace detail diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 061c48853..4f89f3463 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -291,15 +291,17 @@ }); \ j = ((it != std::end(m)) ? it : std::begin(m))->second; \ } \ - template \ + template::value, int> = 0> \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* (for a view, the table holds basic_json values, which compare with the view) */ \ + using nlohmann_json_value_t = nlohmann::detail::json_type_of_t; \ /* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ + static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ + [&j](const std::pair& ej_pair) -> bool \ { \ return ej_pair.second == j; \ }); \ @@ -330,20 +332,22 @@ if (it != std::end(m)) j = it->second; \ else ::nlohmann::detail::templated_json_throw(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range,"enum value out of range for " #ENUM_TYPE, nullptr)); \ } \ - template \ + template::value, int> = 0> \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* (for a view, the table holds basic_json values, which compare with the view) */ \ + using nlohmann_json_value_t = nlohmann::detail::json_type_of_t; \ /* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ + static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ + [&j](const std::pair& ej_pair) -> bool \ { \ return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else ::nlohmann::detail::templated_json_throw(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \ + else ::nlohmann::detail::templated_json_throw(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::from_json_context(j))); \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They @@ -603,6 +607,8 @@ #define NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME(v1, v2) nlohmann_json_t.v2 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(v1, nlohmann_json_default_obj.v2) : nlohmann_json_default_obj.v2; #define NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(...) template::value, int> = 0> __VA_ARGS__ +// from_json() also reads from the views of +#define NLOHMANN_JSON_FROM_TYPE_TEMPLATE(...) template::value, int> = 0> __VA_ARGS__ // Helpers used to dispatch the NLOHMANN_DEFINE_TYPE_*/NLOHMANN_DEFINE_DERIVED_TYPE_* // macros below between a zero-member and a one-or-more-member implementation @@ -655,18 +661,18 @@ */ #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { }) #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -676,7 +682,7 @@ */ #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) @@ -685,7 +691,7 @@ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -712,18 +718,18 @@ */ #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { }) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -733,7 +739,7 @@ */ #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) @@ -742,7 +748,7 @@ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -769,18 +775,18 @@ */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); }) #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -790,7 +796,7 @@ */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) @@ -799,7 +805,7 @@ #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -827,18 +833,18 @@ */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); }) #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -848,7 +854,7 @@ */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) @@ -857,7 +863,7 @@ #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 387691b6b..bd72c3a65 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -340,6 +340,47 @@ template struct negation : std::integral_constant < bool, !B::value > { #endif +/////////////////////// +// json_view helpers // +/////////////////////// + +template +using basic_json_type_t = typename T::basic_json_type; + +template +using materialize_function_t = decltype(std::declval().materialize()); + +// a basic_json_view of (detected by its member type +// basic_json_type, a basic_json, and materialize() returning that type, so that +// json.hpp needs no declaration of the view) +template +struct is_json_view : std::false_type {}; + +template +struct is_json_view>::value>> + : std::is_same, basic_json_type_t> {}; + +// the types from_json() functions of the NLOHMANN_DEFINE_TYPE_* and +// NLOHMANN_JSON_SERIALIZE_ENUM macros read from +template +struct is_basic_json_or_view : disjunction, is_json_view> {}; + +// the basic_json type of a basic_json value or of a view +template +struct json_type_of +{ + using type = T; +}; + +template +struct json_type_of::value>> +{ + using type = typename T::basic_json_type; +}; + +template +using json_type_of_t = typename json_type_of::type; + // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). // This causes compile errors in e.g., Clang 3.5 or GCC 4.9. diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index 430cc598e..5a5f0ed66 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -106,6 +106,8 @@ class basic_json_view using navigation = detail::view::navigation; public: + /// the basic_json type of the document (it also marks views for is_json_view) + using basic_json_type = BasicJsonType; using value_t = detail::value_t; using string_t = typename BasicJsonType::string_t; using number_integer_t = typename BasicJsonType::number_integer_t; @@ -567,8 +569,10 @@ class basic_json_view /// the value converted to T, as BasicJsonType::get(): arithmetic types, /// strings (string_view_t without a copy), std::nullptr_t, std::vector, - /// maps with string keys, and views are converted directly; other types - /// through materialize().get() + /// maps with string keys, and views are converted directly; types whose + /// from_json() accepts views (those of the NLOHMANN_DEFINE_TYPE_* and + /// NLOHMANN_JSON_SERIALIZE_ENUM macros, say) read the view; other types + /// go through materialize().get() template NLOHMANN_VIEW_ALWAYS_INLINE T get() const { @@ -576,11 +580,12 @@ class basic_json_view } /// as BasicJsonType::get_to(): the types get() converts directly are - /// assigned, all others are passed to from_json with v as it is + /// assigned, all others are passed to from_json with v as it is (with + /// the view if from_json() accepts views, else with materialize()) template T& get_to(T& v) const { - get_to_impl(v, detail::view::is_direct_value {}); + get_to_impl(v, get_to_kind {}); return v; } @@ -923,6 +928,26 @@ class basic_json_view return detail::view::map_value>(*this); } + /// whether the from_json() of T's serializer reads views (see + /// detail::has_view_from_json) + template + using reads_view = detail::has_view_from_json, basic_json_view, T>; + + /// how get_to() converts: 2 directly, 1 with a from_json() that reads + /// views, 0 through materialize() + template + using get_to_kind = std::integral_constant < int, detail::view::is_direct_value::value + ? 2 : static_cast(reads_view::value) >; + + /// types whose from_json() reads views, without materialize() + template < typename T, typename std::enable_if < reads_view::value && std::is_default_constructible::value, int >::type = 0 > + T get_impl(detail::view::value_tag /*unused*/, detail::priority_tag<1> /*unused*/) const + { + auto v = T(); + BasicJsonType::template json_serializer::from_json(*this, v); + return v; + } + /// everything else through the BasicJsonType value (from_json included) template T get_impl(detail::view::value_tag /*unused*/, detail::priority_tag<0> /*unused*/) const @@ -933,14 +958,20 @@ class basic_json_view // --- get_to() dispatch --- template - void get_to_impl(T& v, std::true_type /*direct*/) const + void get_to_impl(T& v, std::integral_constant /*direct*/) const { v = get(); } /// from_json may keep parts of v (members a JSON object lacks, say) template - void get_to_impl(T& v, std::false_type /*direct*/) const + void get_to_impl(T& v, std::integral_constant /*from_json reads views*/) const + { + BasicJsonType::template json_serializer::from_json(*this, v); + } + + template + void get_to_impl(T& v, std::integral_constant /*through materialize()*/) const { materialize().get_to(v); } @@ -1645,6 +1676,15 @@ class basic_json_document std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) }; +/*! +@brief whether T is a basic_json_view (of any BasicJsonType and view_mode) + +For constraining templates that accept views, e.g. a from_json() written for +both basic_json values and views. +*/ +template +struct is_json_view : detail::is_json_view {}; + /// a parsed JSON text for json using json_document = basic_json_document; /// a value of a json_document diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bd8a17b23..c0d8d80c9 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -272,6 +272,7 @@ #include // min, transform #include // array +#include // nullptr_t #include // forward_list #include // inserter, front_inserter, end #include // map @@ -2663,15 +2664,17 @@ JSON_HEDLEY_DIAGNOSTIC_POP }); \ j = ((it != std::end(m)) ? it : std::begin(m))->second; \ } \ - template \ + template::value, int> = 0> \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* (for a view, the table holds basic_json values, which compare with the view) */ \ + using nlohmann_json_value_t = nlohmann::detail::json_type_of_t; \ /* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ + static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ + [&j](const std::pair& ej_pair) -> bool \ { \ return ej_pair.second == j; \ }); \ @@ -2702,20 +2705,22 @@ JSON_HEDLEY_DIAGNOSTIC_POP if (it != std::end(m)) j = it->second; \ else ::nlohmann::detail::templated_json_throw(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range,"enum value out of range for " #ENUM_TYPE, nullptr)); \ } \ - template \ + template::value, int> = 0> \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* (for a view, the table holds basic_json values, which compare with the view) */ \ + using nlohmann_json_value_t = nlohmann::detail::json_type_of_t; \ /* NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ + static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ + [&j](const std::pair& ej_pair) -> bool \ { \ return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else ::nlohmann::detail::templated_json_throw(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \ + else ::nlohmann::detail::templated_json_throw(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::from_json_context(j))); \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They @@ -2975,6 +2980,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME(v1, v2) nlohmann_json_t.v2 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(v1, nlohmann_json_default_obj.v2) : nlohmann_json_default_obj.v2; #define NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(...) template::value, int> = 0> __VA_ARGS__ +// from_json() also reads from the views of +#define NLOHMANN_JSON_FROM_TYPE_TEMPLATE(...) template::value, int> = 0> __VA_ARGS__ // Helpers used to dispatch the NLOHMANN_DEFINE_TYPE_*/NLOHMANN_DEFINE_DERIVED_TYPE_* // macros below between a zero-member and a one-or-more-member implementation @@ -3027,18 +3034,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { }) #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3048,7 +3055,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) @@ -3057,7 +3064,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3084,18 +3091,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { }) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3105,7 +3112,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) @@ -3114,7 +3121,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3141,18 +3148,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); }) #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3162,7 +3169,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) @@ -3171,7 +3178,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3199,18 +3206,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }) #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); }) #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__)) #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -3220,7 +3227,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP */ #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }) // identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default #define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) @@ -3229,7 +3236,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \ NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \ - NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) + NLOHMANN_JSON_FROM_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { ::nlohmann::detail::from_json_base(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) }) /*! @brief macro @@ -4516,6 +4523,47 @@ template struct negation : std::integral_constant < bool, !B::value > { #endif +/////////////////////// +// json_view helpers // +/////////////////////// + +template +using basic_json_type_t = typename T::basic_json_type; + +template +using materialize_function_t = decltype(std::declval().materialize()); + +// a basic_json_view of (detected by its member type +// basic_json_type, a basic_json, and materialize() returning that type, so that +// json.hpp needs no declaration of the view) +template +struct is_json_view : std::false_type {}; + +template +struct is_json_view>::value>> + : std::is_same, basic_json_type_t> {}; + +// the types from_json() functions of the NLOHMANN_DEFINE_TYPE_* and +// NLOHMANN_JSON_SERIALIZE_ENUM macros read from +template +struct is_basic_json_or_view : disjunction, is_json_view> {}; + +// the basic_json type of a basic_json value or of a view +template +struct json_type_of +{ + using type = T; +}; + +template +struct json_type_of::value>> +{ + using type = typename T::basic_json_type; +}; + +template +using json_type_of_t = typename json_type_of::type; + // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). // This causes compile errors in e.g., Clang 3.5 or GCC 4.9. @@ -6035,6 +6083,59 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// how to treat decoding errors +/// +/// @ref basic_json::dump uses this to decide what to do with ill-formed +/// UTF-8 while escaping a string, and the binary writers (@ref +/// basic_json::to_cbor, @ref basic_json::to_ubjson, @ref +/// basic_json::to_bjdata, @ref basic_json::to_bson) use it the same way for +/// string values and object keys. The binary readers (@ref +/// basic_json::from_cbor, @ref basic_json::from_msgpack, @ref +/// basic_json::from_ubjson, @ref basic_json::from_bjdata, @ref +/// basic_json::from_bson) use it to decide whether to check text strings +/// and object keys for well-formed UTF-8 at all, since none of those +/// formats requires a decoder to do so. +enum class error_handler_t +{ + strict, ///< throw a type_error/parse_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore, ///< ignore invalid UTF-8 sequences + keep ///< keep invalid UTF-8 sequences unchanged +}; + +/// the default error handler of the CBOR, UBJSON, BJData, and BSON writers: +/// error_handler_t::strict if JSON_STRICT_BINARY_UTF8 is enabled, otherwise +/// error_handler_t::keep (the behavior before version 3.13.0) +constexpr error_handler_t binary_writer_default_error_handler() noexcept +{ +#if JSON_STRICT_BINARY_UTF8 + return error_handler_t::strict; +#else + return error_handler_t::keep; +#endif +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + // #include // #include @@ -6675,16 +6776,114 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p) } #endif -struct from_json_fn +namespace from_json_adl { - template - auto operator()(const BasicJsonType& j, T&& val) const +// hides the library's from_json() overloads above (and the from_json object): +// a call from this namespace finds only the from_json() functions of the +// arguments' own namespaces and classes (ADL); the declaration is never viable +// and never defined (not "= delete", which GCC before 6 reports as a use) +struct hidden; +void from_json(const hidden&); + +// a type no from_json() written for JSON values accepts: a from_json() that +// accepts it is not constrained to them (see has_view_from_json; it lives +// here, so that ADL finds no other from_json() for it) +struct probe +{}; + +struct fn +{ + template + auto operator()(const SourceType& j, T&& val) const noexcept(noexcept(from_json(j, std::forward(val)))) -> decltype(from_json(j, std::forward(val))) { return from_json(j, std::forward(val)); } }; +} // namespace from_json_adl + +// the sources whose from_json() is only looked up by ADL: views (the library's +// overloads are written for basic_json) and the probe +template +struct is_adl_only_from_json_source : disjunction, std::is_same> {}; + +struct from_json_fn +{ + template < typename BasicJsonType, typename T, + enable_if_t < !is_adl_only_from_json_source::value, int > = 0 > + auto operator()(const BasicJsonType& j, T && val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } + + template < typename SourceType, typename T, + enable_if_t < is_adl_only_from_json_source::value, int > = 0 > + auto operator()(const SourceType& j, T && val) const + noexcept(noexcept(from_json_adl::fn {}(j, std::forward(val)))) + -> decltype(from_json_adl::fn {}(j, std::forward(val))) + { + return from_json_adl::fn {}(j, std::forward(val)); + } +}; + +// Whether JSONSerializer::from_json(view, T&) reads T from a view (of +// ) directly: it must accept the view but not the +// probe. An unconstrained from_json() template (written for basic_json, but +// accepting anything) is thus not called with a view, nor are the library's +// own conversions; the view converts such types through materialize(). +template +using serializer_from_json_t = decltype(Serializer::from_json(std::declval(), std::declval())); + +template +struct has_view_from_json + : conjunction < is_detected, + negation>> {}; + +// the base part of a type of the NLOHMANN_DEFINE_DERIVED_TYPE_* macros: from a +// view through its get_to(), which reads the base from the view where the +// base's from_json() accepts views, and from materialize() otherwise +template::value, int> = 0> +void from_json_base(const BasicJsonType& j, T& base) +{ + from_json_fn {}(j, base); +} + +template::value, int> = 0> +void from_json_base(const View& v, T& base) +{ + v.get_to(base); +} + +// the context of an exception thrown by a from_json() of the macros: the value +// itself for basic_json (for JSON_DIAGNOSTICS), none for a view +template::value, int> = 0> +const BasicJsonType * from_json_context(const BasicJsonType& j) noexcept +{ + return &j; +} + +template::value, int> = 0> +std::nullptr_t from_json_context(const View& /*unused*/) noexcept +{ + return nullptr; +} + +// a value as an exception message shows it (invalid UTF-8 replaced; the +// strings of a view are always valid UTF-8) +template::value, int> = 0> +typename BasicJsonType::string_t from_json_dump(const BasicJsonType& j) +{ + return j.dump(-1, ' ', false, error_handler_t::replace); +} + +template::value, int> = 0> +typename View::string_t from_json_dump(const View& v) +{ + return v.dump(); +} } // namespace detail @@ -6779,57 +6978,6 @@ NLOHMANN_JSON_NAMESPACE_END // #include // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/// how to treat decoding errors -/// -/// @ref basic_json::dump uses this to decide what to do with ill-formed -/// UTF-8 while escaping a string, and the binary writers (@ref -/// basic_json::to_cbor, @ref basic_json::to_ubjson, @ref -/// basic_json::to_bjdata, @ref basic_json::to_bson) use it the same way for -/// string values and object keys. The binary readers (@ref -/// basic_json::from_cbor, @ref basic_json::from_msgpack, @ref -/// basic_json::from_ubjson, @ref basic_json::from_bjdata, @ref -/// basic_json::from_bson) use it to decide whether to check text strings -/// and object keys for well-formed UTF-8 at all, since none of those -/// formats requires a decoder to do so. -enum class error_handler_t -{ - strict, ///< throw a type_error/parse_error exception in case of invalid UTF-8 - replace, ///< replace invalid UTF-8 sequences with U+FFFD - ignore, ///< ignore invalid UTF-8 sequences - keep ///< keep invalid UTF-8 sequences unchanged -}; - -/// the default error handler of the CBOR, UBJSON, BJData, and BSON writers: -/// error_handler_t::strict if JSON_STRICT_BINARY_UTF8 is enabled, otherwise -/// error_handler_t::keep (the behavior before version 3.13.0) -constexpr error_handler_t binary_writer_default_error_handler() noexcept -{ -#if JSON_STRICT_BINARY_UTF8 - return error_handler_t::strict; -#else - return error_handler_t::keep; -#endif -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_BEGIN diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index c1f19c6c5..5cb16ced2 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -7833,6 +7833,8 @@ class basic_json_view using navigation = detail::view::navigation; public: + /// the basic_json type of the document (it also marks views for is_json_view) + using basic_json_type = BasicJsonType; using value_t = detail::value_t; using string_t = typename BasicJsonType::string_t; using number_integer_t = typename BasicJsonType::number_integer_t; @@ -8294,8 +8296,10 @@ class basic_json_view /// the value converted to T, as BasicJsonType::get(): arithmetic types, /// strings (string_view_t without a copy), std::nullptr_t, std::vector, - /// maps with string keys, and views are converted directly; other types - /// through materialize().get() + /// maps with string keys, and views are converted directly; types whose + /// from_json() accepts views (those of the NLOHMANN_DEFINE_TYPE_* and + /// NLOHMANN_JSON_SERIALIZE_ENUM macros, say) read the view; other types + /// go through materialize().get() template NLOHMANN_VIEW_ALWAYS_INLINE T get() const { @@ -8303,11 +8307,12 @@ class basic_json_view } /// as BasicJsonType::get_to(): the types get() converts directly are - /// assigned, all others are passed to from_json with v as it is + /// assigned, all others are passed to from_json with v as it is (with + /// the view if from_json() accepts views, else with materialize()) template T& get_to(T& v) const { - get_to_impl(v, detail::view::is_direct_value {}); + get_to_impl(v, get_to_kind {}); return v; } @@ -8650,6 +8655,26 @@ class basic_json_view return detail::view::map_value>(*this); } + /// whether the from_json() of T's serializer reads views (see + /// detail::has_view_from_json) + template + using reads_view = detail::has_view_from_json, basic_json_view, T>; + + /// how get_to() converts: 2 directly, 1 with a from_json() that reads + /// views, 0 through materialize() + template + using get_to_kind = std::integral_constant < int, detail::view::is_direct_value::value + ? 2 : static_cast(reads_view::value) >; + + /// types whose from_json() reads views, without materialize() + template < typename T, typename std::enable_if < reads_view::value && std::is_default_constructible::value, int >::type = 0 > + T get_impl(detail::view::value_tag /*unused*/, detail::priority_tag<1> /*unused*/) const + { + auto v = T(); + BasicJsonType::template json_serializer::from_json(*this, v); + return v; + } + /// everything else through the BasicJsonType value (from_json included) template T get_impl(detail::view::value_tag /*unused*/, detail::priority_tag<0> /*unused*/) const @@ -8660,14 +8685,20 @@ class basic_json_view // --- get_to() dispatch --- template - void get_to_impl(T& v, std::true_type /*direct*/) const + void get_to_impl(T& v, std::integral_constant /*direct*/) const { v = get(); } /// from_json may keep parts of v (members a JSON object lacks, say) template - void get_to_impl(T& v, std::false_type /*direct*/) const + void get_to_impl(T& v, std::integral_constant /*from_json reads views*/) const + { + BasicJsonType::template json_serializer::from_json(*this, v); + } + + template + void get_to_impl(T& v, std::integral_constant /*through materialize()*/) const { materialize().get_to(v); } @@ -9372,6 +9403,15 @@ class basic_json_document std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) }; +/*! +@brief whether T is a basic_json_view (of any BasicJsonType and view_mode) + +For constraining templates that accept views, e.g. a from_json() written for +both basic_json values and views. +*/ +template +struct is_json_view : detail::is_json_view {}; + /// a parsed JSON text for json using json_document = basic_json_document; /// a value of a json_document diff --git a/tests/benchmarks/json_view/README.md b/tests/benchmarks/json_view/README.md index c4a05d35a..a0bd2afbc 100644 --- a/tests/benchmarks/json_view/README.md +++ b/tests/benchmarks/json_view/README.md @@ -59,6 +59,12 @@ An editable `json_document` edits in place; yyjson copies its immutable document (`yyjson_doc_mut_copy`); Boost.JSON and `json::parse` build mutable DOMs; simdjson cannot edit a document. All outputs are checked to describe the same value. +`bench_decode.cpp` measures decoding into structs whose `from_json()` comes from the `NLOHMANN_DEFINE_TYPE_*` macros +(twitter statuses, citm_catalog performances, and 20,000 synthetic records): `get()` on a view, which reads the +view directly, against `materialize().get()`, `json::parse` plus `get()`, and `get()` on an already parsed +`json` value. Only nlohmann/json takes part, as the other libraries have no comparable mapping; all engines are +checked to decode the same values. + Before anything is timed, all engines must accept each document and agree on the traversal: the number of values, the bytes of all strings and keys, and the sum of all numbers. All engines run interleaved in every round, and the best round is reported, as time and as a factor of the `json_view` time (below 1 means faster than `json_view`). Each timed diff --git a/tests/benchmarks/json_view/bench_decode.cpp b/tests/benchmarks/json_view/bench_decode.cpp new file mode 100644 index 000000000..29e3d7f81 --- /dev/null +++ b/tests/benchmarks/json_view/bench_decode.cpp @@ -0,0 +1,312 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +// Decoding into structs: the same types, with from_json() generated by the +// NLOHMANN_DEFINE_TYPE_* macros, read three ways. Only nlohmann/json takes +// part; the other libraries have no comparable mapping. +// +// view json_document::parse, then get() on the view (from_json reads the view) +// materialize json_document::parse, then materialize().get() (a json value first) +// json::parse json::parse, then get() +// json::get get() on an already parsed json value (the decoding alone) +// +// Documents: twitter (the statuses with their users and hashtags), citm_catalog +// (the performances with their seat categories), and a synthetic document of +// 20,000 nested records. +// +// Build: see README.md (same flags as bench_view.cpp). +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using nlohmann::json; +using nlohmann::json_document; + +static volatile std::size_t g_sink; + +namespace twitter +{ +struct hashtag +{ + std::string text; + std::vector indices; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(hashtag, text, indices) + +struct entities +{ + std::vector hashtags; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(entities, hashtags) + +struct user +{ + std::uint64_t id = 0; + std::string screen_name; + std::string description; + std::int64_t followers_count = 0; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(user, id, screen_name, description, followers_count) + +struct status +{ + std::uint64_t id = 0; + std::string text; + user user; + entities entities; + std::int64_t retweet_count = 0; + std::int64_t favorite_count = 0; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(status, id, text, user, entities, retweet_count, favorite_count) + +struct document +{ + std::vector statuses; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(document, statuses) + +std::size_t digest(const document& d) +{ + std::size_t n = 0; + for (const auto& s : d.statuses) + { + n += s.text.size() + s.user.screen_name.size() + s.entities.hashtags.size() + static_cast(s.retweet_count); + } + return n; +} +} // namespace twitter + +namespace citm +{ +struct area +{ + std::int64_t areaId = 0; + std::vector blockIds; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(area, areaId, blockIds) + +struct seat_category +{ + std::vector areas; + std::int64_t seatCategoryId = 0; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(seat_category, areas, seatCategoryId) + +struct performance +{ + std::int64_t id = 0; + std::int64_t eventId = 0; + std::vector seatCategories; + std::int64_t start = 0; + std::string venueCode; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(performance, id, eventId, seatCategories, start, venueCode) + +struct document +{ + std::vector performances; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(document, performances) + +std::size_t digest(const document& d) +{ + std::size_t n = 0; + for (const auto& p : d.performances) + { + n += p.seatCategories.size() + p.venueCode.size() + static_cast(p.start % 1000); + for (const auto& c : p.seatCategories) + { + n += c.areas.size(); + } + } + return n; +} +} // namespace citm + +namespace synthetic +{ +struct position +{ + double x = 0; + double y = 0; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(position, x, y) + +struct child +{ + std::int64_t id = 0; + std::string label; + bool active = false; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(child, id, label, active) + +struct record +{ + std::int64_t id = 0; + std::string name; + std::vector tags; + position pos; + std::vector children; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(record, id, name, tags, pos, children) + +struct document +{ + std::vector records; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(document, records) + +std::string make_text(std::size_t n) +{ + std::string s = "{\"records\":["; + for (std::size_t i = 0; i < n; ++i) + { + const std::string id = std::to_string(i); + s += (i == 0 ? "" : ","); + s += "{\"id\":" + id + ",\"name\":\"record number " + id + "\",\"tags\":[\"alpha\",\"beta\",\"gamma\"],"; + s += "\"pos\":{\"x\":" + id + ".25,\"y\":-" + id + ".5},\"children\":["; + for (std::size_t k = 0; k < 3; ++k) + { + s += (k == 0 ? "" : ","); + s += "{\"id\":" + std::to_string(i * 3 + k) + ",\"label\":\"child " + std::to_string(k) + "\",\"active\":" + (k % 2 == 0 ? "true" : "false") + "}"; + } + s += "]}"; + } + s += "]}"; + return s; +} + +std::size_t digest(const document& d) +{ + std::size_t n = 0; + for (const auto& r : d.records) + { + n += r.name.size() + r.tags.size() + r.children.size() + static_cast(r.pos.x); + } + return n; +} +} // namespace synthetic + +namespace +{ +std::string read_file(const std::string& p) +{ + std::ifstream f(p, std::ios::binary); + if (!f) + { + std::fprintf(stderr, "cannot open %s\n", p.c_str()); + std::exit(1); + } + std::stringstream ss; + ss << f.rdbuf(); + return ss.str(); +} + +double time_ns(const std::function& f) +{ + const auto t0 = std::chrono::steady_clock::now(); + f(); + const auto t1 = std::chrono::steady_clock::now(); + return static_cast(std::chrono::duration_cast(t1 - t0).count()); +} + +struct engine +{ + std::string name; + std::function run; +}; + +template +std::vector engines(const std::string& text, const json& parsed) +{ + return + { + {"view", [&text] { const json_document d = json_document::parse(text); return digest(d.root().get()); }}, + {"materialize", [&text] { const json_document d = json_document::parse(text); return digest(d.root().materialize().get()); }}, + {"json::parse", [&text] { return digest(json::parse(text).get()); }}, + {"json::get", [&parsed] { return digest(parsed.get()); }}, + }; +} +} // namespace + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::fprintf(stderr, "usage: %s [rounds]\n", argv[0]); + return 1; + } + const std::string dir = argv[1]; + const int rounds = argc > 2 ? std::max(1, std::atoi(argv[2])) : 30; + + struct document_case + { + std::string name; + std::string text; + json parsed; + std::vector engines; + }; + std::vector cases; + cases.push_back({"twitter", read_file(dir + "/nativejson-benchmark/twitter.json"), json(), {}}); + cases.push_back({"citm_catalog", read_file(dir + "/nativejson-benchmark/citm_catalog.json"), json(), {}}); + cases.push_back({"synthetic", synthetic::make_text(20000), json(), {}}); + for (auto& c : cases) + { + c.parsed = json::parse(c.text); + } + cases[0].engines = engines(cases[0].text, cases[0].parsed); + cases[1].engines = engines(cases[1].text, cases[1].parsed); + cases[2].engines = engines(cases[2].text, cases[2].parsed); + + std::FILE* csv = std::fopen("bench_decode.csv", "w"); + if (csv == nullptr) + { + std::perror("bench_decode.csv"); + return 1; + } + std::fprintf(csv, "doc,bytes,engine,ns\n"); + for (const auto& c : cases) + { + // all engines must decode the same values + const std::size_t expected = c.engines[0].run(); + bool ok = true; + for (const auto& e : c.engines) + { + ok = ok && e.run() == expected; + } + std::vector best(c.engines.size(), 1e300); + for (int r = 0; r < rounds; ++r) + { + for (std::size_t k = 0; k < c.engines.size(); ++k) + { + g_sink = c.engines[k].run(); // untimed: see bench_view.cpp + best[k] = std::min(best[k], time_ns([&] { g_sink = c.engines[k].run(); })); + } + } + std::printf("%-13s %-7s %s", c.name.c_str(), "decode", ok ? "" : "[MISMATCH] "); + for (std::size_t k = 0; k < c.engines.size(); ++k) + { + const double us = best[k] / 1000.0; + std::printf(" %s %.*fus (%.2fx)", c.engines[k].name.c_str(), us < 10 ? 3 : (us < 1000 ? 1 : 0), us, best[k] / best[0]); + std::fprintf(csv, "%s,%zu,%s,%.1f\n", c.name.c_str(), c.text.size(), c.engines[k].name.c_str(), best[k]); + } + std::printf("\n"); + } + std::fclose(csv); + return 0; +} diff --git a/tests/benchmarks/json_view/compare.py b/tests/benchmarks/json_view/compare.py index 39d5048f0..6573c88af 100755 --- a/tests/benchmarks/json_view/compare.py +++ b/tests/benchmarks/json_view/compare.py @@ -9,7 +9,7 @@ """Compare json_view with yyjson, simdjson, Boost.JSON, and json::parse. -Builds bench_view.cpp, bench_corpus.cpp, and bench_edit.cpp against the include/ directory of +Builds bench_view.cpp, bench_corpus.cpp, bench_edit.cpp, and bench_decode.cpp against the include/ directory of this checkout, runs them, and writes the results with everything needed to reproduce them (date, commit, CPU, OS, compiler, library versions, flags) to results/-.md and .csv next to this script. @@ -283,7 +283,7 @@ def main(): objects.append(obj) binaries = {} - for bench in ['bench_view', 'bench_corpus', 'bench_edit']: + for bench in ['bench_view', 'bench_corpus', 'bench_edit', 'bench_decode']: exe = os.path.join(args.build_dir, bench) run(cxx + flags + include + [os.path.join(HERE, bench + '.cpp')] + objects + link + ['-o', exe]) binaries[bench] = exe @@ -297,6 +297,8 @@ def main(): capture_output=True, text=True).stdout outputs['bench_edit'] = run([binaries['bench_edit'], args.data, str(max(1, args.rounds // 2))], cwd=args.build_dir, capture_output=True, text=True).stdout + outputs['bench_decode'] = run([binaries['bench_decode'], args.data, str(max(1, args.rounds // 2))], cwd=args.build_dir, + capture_output=True, text=True).stdout for name, text in outputs.items(): print(text) @@ -330,7 +332,7 @@ def main(): f.write(f'\n## {name}\n\n```\n{text.rstrip()}\n```\n') with open(stem + '.csv', 'w', encoding='utf-8') as out: out.write(''.join(f'# {key}: {value}\n' for key, value in meta)) - for name in ['bench_view', 'bench_corpus', 'bench_edit']: + for name in ['bench_view', 'bench_corpus', 'bench_edit', 'bench_decode']: path = os.path.join(args.build_dir, name + '.csv') if os.path.isfile(path): with open(path, encoding='utf-8') as f: diff --git a/tests/src/unit-json_view_from_json.cpp b/tests/src/unit-json_view_from_json.cpp new file mode 100644 index 000000000..f106b1fe0 --- /dev/null +++ b/tests/src/unit-json_view_from_json.cpp @@ -0,0 +1,357 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#include +using nlohmann::json; +using nlohmann::json_document; +using nlohmann::json_view; +using nlohmann::json_editable_document; +using nlohmann::ordered_json_document; + +#include "json_view_test_helpers.hpp" +#if !defined(JSON_NOEXCEPTION) + using json_view_test::exception_of_call; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef JSON_HAS_CPP_17 + #include +#endif + +// The from_json() functions of the NLOHMANN_DEFINE_TYPE_* and +// NLOHMANN_JSON_SERIALIZE_ENUM macros read views directly; other from_json() +// functions get the value of materialize(). + +namespace view_from_json +{ +struct point +{ + int x = 0; + int y = 0; + NLOHMANN_DEFINE_TYPE_INTRUSIVE(point, x, y) +}; + +struct shape +{ + std::string name; + std::vector points; + point center; + std::map named; + NLOHMANN_DEFINE_TYPE_INTRUSIVE(shape, name, points, center, named) +}; + +struct with_defaults +{ + int a = 1; + std::string b = "default"; + std::vector c = {1, 2}; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(with_defaults, a, b, c) + +struct with_names +{ + int value = 0; + std::string text; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(with_names, "Value", value, "Text", text) + +struct empty +{ + NLOHMANN_DEFINE_TYPE_INTRUSIVE(empty) +}; + +enum class color +{ + red, + green, + blue +}; +NLOHMANN_JSON_SERIALIZE_ENUM(color, +{ + {color::red, "red"}, + {color::green, "green"}, + {color::blue, nullptr}, +}) + +enum class strict_color +{ + red, + green +}; +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_color, +{ + {strict_color::red, "red"}, + {strict_color::green, 1}, +}) + +struct base +{ + int id = 0; + NLOHMANN_DEFINE_TYPE_INTRUSIVE(base, id) +}; + +struct derived : base +{ + color c = color::red; + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(derived, base, c) +}; + +struct derived_with_defaults : base +{ + std::string label = "none"; + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(derived_with_defaults, base, label) +}; + +// a base whose from_json() is written for json only: the derived type reads +// its own members from the view and the base part from materialize() +struct legacy_base +{ + int id = 0; +}; +// NOLINTNEXTLINE(misc-use-internal-linkage): found by ADL, like the from_json() functions of users +inline void from_json(const json& j, legacy_base& b) +{ + b.id = j.at("id").get(); +} + +struct derived_from_legacy : legacy_base +{ + int extra = 0; +}; +NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(derived_from_legacy, legacy_base, extra) + +// from_json() for json only: converted through materialize() +struct legacy +{ + int v = 0; +}; +// NOLINTNEXTLINE(misc-use-internal-linkage): found by ADL, like the from_json() functions of users +inline void from_json(const json& j, legacy& l) +{ + l.v = j.at("v").get(); +} + +// an unconstrained from_json() template, as in the documentation of +// arbitrary type conversions: converted through materialize() +struct unconstrained +{ + int v = 0; +}; +template +// NOLINTNEXTLINE(misc-use-internal-linkage): found by ADL, like the from_json() functions of users +void from_json(const BasicJsonType& j, unconstrained& u) +{ + u.v = j.at("v").template get(); +} + +// a from_json() constrained by the user to views and json: reads the view +struct constrained +{ + int v = 0; +}; +template < typename J, typename std::enable_if < nlohmann::is_json_view::value || std::is_same::value, int >::type = 0 > +// NOLINTNEXTLINE(misc-use-internal-linkage): found by ADL, like the from_json() functions of users +void from_json(const J& j, constrained& c) +{ + c.v = j.at("v").template get(); +} +} // namespace view_from_json + +namespace +{ +template +struct reads_view : nlohmann::detail::has_view_from_json, View, T> {}; +} // namespace + +TEST_CASE("json_view from_json") +{ + using namespace view_from_json; // NOLINT(google-build-using-namespace) + + SECTION("which from_json() functions read views") + { + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "macro types read views"); + static_assert(reads_view::value, "enum macro types read views"); + static_assert(reads_view::value, "enum macro types read views"); + static_assert(reads_view::value, "constrained from_json() reads views"); + static_assert(reads_view::value, "editable views as well"); + static_assert(reads_view::value, "views of ordered_json as well"); + static_assert(!reads_view::value, "from_json() for json"); + static_assert(!reads_view::value, "from_json() for json"); + static_assert(!reads_view::value, "unconstrained from_json()"); + + static_assert(nlohmann::is_json_view::value, "is_json_view"); + static_assert(nlohmann::is_json_view::value, "is_json_view"); + static_assert(!nlohmann::is_json_view::value, "is_json_view"); + static_assert(!nlohmann::is_json_view::value, "is_json_view"); + static_assert(!nlohmann::is_json_view::value, "is_json_view"); + } + + SECTION("the library's own conversions are not called with a view (and the probe compiles for them)") + { + // these go through materialize() (vectors, maps with string keys, + // strings, and numbers are converted by the view itself) + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view>::value, "std types"); + static_assert(!reads_view::value, "std types"); + static_assert(!reads_view::value, "std types"); + static_assert(!reads_view::value, "std types"); + static_assert(!reads_view::value, "std types"); + static_assert(!reads_view::value, "std types"); + static_assert(!reads_view::value, "std types"); + static_assert(!reads_view::value, "std types"); +#ifdef JSON_HAS_CPP_17 + static_assert(!reads_view>::value, "std types"); +#endif + CHECK(true); + } + + SECTION("macro types") + { + const json_document d = json_document::parse(R"({"name": "s", "points": [{"x": 1, "y": 2}, {"x": 3, "y": 4}], + "center": {"x": 5, "y": 6}, "named": {"a": {"x": 7, "y": 8}}})"); + const shape s = d.root().get(); + CHECK(s.name == "s"); + CHECK(s.points.size() == 2); + CHECK(s.points[1].y == 4); + CHECK(s.center.x == 5); + CHECK(s.named.at("a").y == 8); + + point p; + d.root()["center"].get_to(p); + CHECK(p.x == 5); + CHECK(p.y == 6); + + static_cast(d.root()["center"].get()); + } + + SECTION("defaults and names") + { + const json_document d = json_document::parse(R"({"a": 5, "Value": 3, "Text": "t"})"); + const with_defaults w = d.root().get(); + CHECK(w.a == 5); + CHECK(w.b == "default"); + CHECK(w.c == std::vector {1, 2}); + const json_document null_document = json_document::parse("null"); + CHECK(null_document.root().get().a == 1); + const with_names n = d.root().get(); + CHECK(n.value == 3); + CHECK(n.text == "t"); + } + + SECTION("enums") + { + const json_document d = json_document::parse(R"(["green", null, "purple", 1, "red"])"); + CHECK(d.root()[0].get() == color::green); + CHECK(d.root()[1].get() == color::blue); + CHECK(d.root()[2].get() == color::red); // unknown values map to the first entry + CHECK(d.root()[3].get() == strict_color::green); + CHECK(d.root()[4].get() == strict_color::red); +#if !defined(JSON_NOEXCEPTION) + const json j = d.root().materialize(); + CHECK(exception_of_call([&] { static_cast(d.root()[2].get()); }) == "[json.exception.out_of_range.410] enum value out of range for strict_color: \"purple\""); +#if !JSON_DIAGNOSTICS + CHECK(exception_of_call([&] { static_cast(d.root()[2].get()); }) == exception_of_call([&] { static_cast(j[2].get()); })); +#endif +#endif + } + + SECTION("derived types") + { + const json_document d = json_document::parse(R"({"id": 7, "c": "green", "extra": 3})"); + const derived x = d.root().get(); + CHECK(x.id == 7); + CHECK(x.c == color::green); + const derived_with_defaults y = d.root().get(); + CHECK(y.id == 7); + CHECK(y.label == "none"); + const derived_from_legacy z = d.root().get(); + CHECK(z.id == 7); + CHECK(z.extra == 3); + } + + SECTION("other from_json() functions get the value of materialize()") + { + const json_document d = json_document::parse(R"({"v": 4})"); + CHECK(d.root().get().v == 4); + CHECK(d.root().get().v == 4); + CHECK(d.root().get().v == 4); + legacy l; + d.root().get_to(l); + CHECK(l.v == 4); + } + + SECTION("duplicate keys: members are looked up in the view, where the first one wins") + { + const json_document d = json_document::parse(R"({"x": 1, "y": 2, "x": 3})"); + CHECK(d.root().get().x == 1); + // materialize() keeps the last value, like parse() + CHECK(d.root().materialize().get().x == 3); + } + + SECTION("editable documents and ordered_json") + { + json_editable_document e = json_editable_document::parse(R"({"x": 1, "y": 2})"); + e.set(e.root(), "x", 10); + CHECK(e.root().get().x == 10); + const ordered_json_document o = ordered_json_document::parse(R"({"name": "o", "points": [], "center": {"x": 1, "y": 2}, "named": {}})"); + CHECK(o.root().get().center.y == 2); + } + +#if !defined(JSON_NOEXCEPTION) && !JSON_DIAGNOSTICS + SECTION("errors are those of the same conversion of a json value") + { + // (with JSON_DIAGNOSTICS, those of json name the path of the value) + const json_document d = json_document::parse(R"([{"x": 1}, {"x": "1", "y": 2}, [1, 2], {"id": "7", "c": "red"}])"); + const json j = d.root().materialize(); + for (std::size_t i = 0; i < 3; ++i) + { + CAPTURE(i) + CHECK(exception_of_call([&] { static_cast(d.root()[i].get()); }) == exception_of_call([&] { static_cast(j[i].get()); })); + } + CHECK(exception_of_call([&] { static_cast(d.root()[3].get()); }) == exception_of_call([&] { static_cast(j[3].get()); })); + } +#endif +}