94 lines
5.0 KiB
Markdown
94 lines
5.0 KiB
Markdown
# Writing generic code
|
|
|
|
## View types
|
|
|
|
A view's type names its `BasicJsonType` and its [`view_mode`](../../api/view_mode.md):
|
|
[`json_view`](../../api/json_view.md) is `#!cpp basic_json_view<json, view_mode::read_only>`, and
|
|
[`json_editable_view`](../../api/json_editable_view.md) is `#!cpp basic_json_view<json, view_mode::editable>`. These
|
|
are different types. The only conversion between them turns a view of a read-only document into a view of an
|
|
editable document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
|
|
|
|
```cpp
|
|
void print_names(json_view v); // accepts neither an ordered_json_view nor a json_editable_view
|
|
void print_names(json_editable_view v); // also accepts a json_view, but no ordered_json_view
|
|
```
|
|
|
|
This is the same pitfall as a `to_json`/`from_json` written for `json` instead of any `BasicJsonType`, which then does
|
|
not work for `ordered_json`. The shortcuts are for declaring documents and views; a function that takes a view names
|
|
the views it accepts with a template:
|
|
|
|
```cpp
|
|
// views of editable documents only
|
|
template<typename BasicJsonType>
|
|
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
|
|
|
|
// views of read-only documents only
|
|
template<typename BasicJsonType>
|
|
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
|
|
|
|
// views of either document type
|
|
template<typename BasicJsonType, view_mode Mode>
|
|
void f(basic_json_view<BasicJsonType, Mode> v);
|
|
```
|
|
|
|
Views are passed by value: they are two pointers, cheap to copy, like a `#!cpp std::string_view`. A `const&` works as
|
|
well, but a non-`const` reference does not bind to the views that
|
|
[`root()`](../../api/basic_json_document/root.md), [`operator[]`](../../api/basic_json_view/operator%5B%5D.md), and
|
|
[`at()`](../../api/basic_json_view/at.md) return, so `#!cpp f(doc.root())` would not compile.
|
|
|
|
There is no conversion from an editable view to a read-only one, by design: the mode decides how a view walks the
|
|
index. A read-only view reads a container in place, while a view of an editable document follows the links that edits
|
|
leave behind, so a read-only view of an edited document would read stale values. The other direction is safe: a
|
|
read-only document has no edits, so a view of it [converts](../../api/basic_json_view/basic_json_view.md) to an
|
|
editable view, which reads it like an editable document before its first edit. A template deduces the mode without
|
|
conversions, so the template forms above still accept only the modes they name. Views of both modes compare with
|
|
[`operator==`](../../api/basic_json_view/operator_eq.md).
|
|
|
|
[`is_json_view<T>`](../../api/is_json_view.md) tells views from other types, for constraints that cannot be written as
|
|
a `basic_json_view<...>` parameter.
|
|
|
|
## A `from_json()` that reads views
|
|
|
|
[`get<T>()`](../../api/basic_json_view/get.md) on a view calls `T`'s `from_json()` with the view itself if that
|
|
`from_json()` accepts views, and with [`materialize()`](../../api/basic_json_view/materialize.md) otherwise. The
|
|
`from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](../../api/macros/nlohmann_define_type_intrusive.md),
|
|
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../../api/macros/nlohmann_define_derived_type.md), and
|
|
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../../api/macros/nlohmann_json_serialize_enum.md) macros, and those of the library
|
|
for the standard types, accept views, so these types need nothing else. For decoding the statuses of
|
|
twitter.json into structs, reading the view is about 5 times faster than `#!cpp materialize().get<T>()`.
|
|
|
|
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
|
|
|
|
```cpp
|
|
template < typename J, typename std::enable_if <
|
|
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
|
|
void from_json(const J& j, person& p)
|
|
{
|
|
p.name = j.at("name").template get<std::string>();
|
|
p.age = j.at("age").template get<int>();
|
|
}
|
|
```
|
|
|
|
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`,
|
|
`contains()`, `value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained
|
|
`#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in
|
|
[arbitrary type conversions](../arbitrary_types.md), is never called with a view, as it might use `basic_json`
|
|
functions a view does not have; such a type is converted through `materialize()`. A `from_json()` for `json` only is
|
|
converted through `materialize()` as well.
|
|
|
|
Reading the view and `#!cpp materialize().get<T>()` give the same result, except for an object with a repeated key;
|
|
see [Duplicate keys](differences.md#duplicate-keys).
|
|
|
|
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
|
|
|
## See also
|
|
|
|
- [Zero-copy JSON views](index.md) - overview
|
|
- [`is_json_view`](../../api/is_json_view.md) - type trait to detect views
|
|
- [`view_mode`](../../api/view_mode.md) - read-only and editable views
|
|
- [Reading values](reading.md) - what `get<T>()` converts without materializing
|
|
|
|
## Version history
|
|
|
|
- Added in version 3.13.0.
|