mirror of
https://github.com/nlohmann/json.git
synced 2026-11-03 03:15:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7a7534872 | ||
|
|
eaee875178 | ||
|
|
3724295333 |
No files matched your search
@@ -50,7 +50,7 @@ Besides `basic_json`, the library offers a second way to hold a parsed JSON text
|
||||
[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the
|
||||
values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document.
|
||||
Both are defined in the header `<nlohmann/json_view.hpp>`; see
|
||||
[Zero-copy JSON views](../../features/json_view/index.md) for the concept and the trade-offs.
|
||||
[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs.
|
||||
|
||||
- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only
|
||||
documents of the `basic_json` specialization they belong to
|
||||
@@ -363,7 +363,7 @@ Access to the JSON value
|
||||
specification of the JSON data interchange format
|
||||
- [`basic_json_document`](../basic_json_document/index.md) - / [`basic_json_view`](../basic_json_view/index.md) - parse a JSON
|
||||
text into a flat index and read it through a view
|
||||
- [Zero-copy JSON views](../../features/json_view/index.md) - when to use a document and a view instead of a `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view.md) - when to use a document and a view instead of a `basic_json`
|
||||
- [Types](../../features/types/index.md) - how JSON values are stored and which types exist
|
||||
- [Converting values](../../features/conversions.md) - get values out of a JSON value and put values in
|
||||
- [Creating JSON values](../../features/creating_values.md) - all the ways to build a JSON value
|
||||
|
||||
@@ -124,7 +124,7 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
||||
[`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but
|
||||
keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses
|
||||
less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string,
|
||||
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view/index.md).
|
||||
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view.md).
|
||||
|
||||
!!! tip "Performance"
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ compile; see [`parse`](parse.md#notes).
|
||||
|
||||
- [`parse`](parse.md) - deserialize from a compatible input
|
||||
- [`BasicJsonType::accept`](../basic_json/accept.md) - the corresponding function of `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how parsing a document differs from, and matches, basic_json::parse
|
||||
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how parsing a document differs from, and matches, basic_json::parse
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ Constant.
|
||||
|
||||
- [`parse`](parse.md) - deserialize from a compatible input
|
||||
- [`is_discarded`](is_discarded.md) - return whether the last parse failed
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ by an erase, not only those at or after the removed element, since the container
|
||||
- [`root`](root.md) - the view of the root value, the starting point of overload 3
|
||||
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
|
||||
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ points apply to every edit:
|
||||
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
|
||||
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
|
||||
number that was not itself edited — see [Editing a document](../../features/json_view/editing.md) for
|
||||
number that was not itself edited — see [Editing a document](../../features/json_view.md#editing-a-document) for
|
||||
why this matters.
|
||||
- [`read()`](read.md) discards all edits, [`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once
|
||||
there are edits, and [`memory_usage()`](memory_usage.md) includes the memory edits use.
|
||||
@@ -118,7 +118,7 @@ points apply to every edit:
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
|
||||
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
|
||||
- [`basic_json_view`](../basic_json_view/index.md) - the non-owning handle to a value in a document
|
||||
- [`json_document`](../json_document.md) - and [`json_editable_document`](../json_editable_document.md) - the default specializations
|
||||
- [`materialize`](../basic_json_view/materialize.md) - build a `basic_json` value from a view
|
||||
|
||||
@@ -108,7 +108,7 @@ an edit in general.
|
||||
- [`set`](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
|
||||
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ When the document is discarded, [`root()`](root.md) returns a discarded view (it
|
||||
|
||||
- [`parse`](parse.md) - deserialize from a compatible input
|
||||
- [`basic_json_view::is_discarded`](../basic_json_view/is_discarded.md) - return whether a view is invalid
|
||||
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how a discarded document arises
|
||||
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how a discarded document arises
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ static basic_json_document load(std::vector<std::uint8_t>&& image,
|
||||
|
||||
In every overload, the node index is copied into storage the document itself owns — so that it is properly aligned,
|
||||
and, for an [editable](index.md#edits) document, can be edited — while the text and the decoded strings stay in
|
||||
`image`. The hash indexes [large objects](../../features/json_view/index.md) use for lookup are rebuilt, exactly as after
|
||||
`image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after
|
||||
parsing.
|
||||
|
||||
## Parameters
|
||||
@@ -172,7 +172,7 @@ returned document, and any [view](../basic_json_view/index.md) taken from it, is
|
||||
- [`save`](save.md) - write the document as an image
|
||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [`parse`](parse.md) - deserialize from JSON text instead of an image
|
||||
- [Images](../../features/json_view/images.md) - why and when to use images
|
||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ to observe the effect of [`shrink_to_fit()`](shrink_to_fit.md).
|
||||
|
||||
- [`node_count`](node_count.md) - the number of index entries
|
||||
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
|
||||
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
|
||||
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ strings and, for an owned document, the text).
|
||||
|
||||
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
|
||||
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
|
||||
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
|
||||
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ document (`#!cpp owns_source() == false`) is only valid while the buffer it was
|
||||
- [`parse`](parse.md) - deserialize from a compatible input
|
||||
- [`parse_copy`](parse_copy.md) - deserialize a copy of a compatible input, always owned
|
||||
- [`source`](source.md) - the parsed text
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ told apart from other class-type iterators, so their range is read into an owned
|
||||
range (e.g. of a `#!cpp std::list<char>`).
|
||||
|
||||
See [`owns_source`](owns_source.md) to check which happened after a call, and the
|
||||
[feature page](../../features/json_view/index.md) for the reasoning.
|
||||
[feature page](../../features/json_view.md) for the reasoning.
|
||||
|
||||
**Numbers.** As for [`BasicJsonType::parse()`](../basic_json/parse.md), an integer literal too large for the 64-bit
|
||||
integer type becomes a floating-point value.
|
||||
|
||||
@@ -74,7 +74,7 @@ compile; see [`parse`](parse.md#notes).
|
||||
|
||||
- [`parse`](parse.md) - deserialize from a compatible input, borrowing it where possible
|
||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - borrowing or copying the source text
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - borrowing or copying the source text
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ Like [`set`](set.md) on a member or an element, `push_back` never moves an exist
|
||||
- [`root`](root.md) - the view of the root value
|
||||
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
|
||||
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ compile; see [`parse`](parse.md#notes).
|
||||
- [`parse`](parse.md) - deserialize from a compatible input
|
||||
- [`root`](root.md) - the view of the root value
|
||||
- [`load`](load.md) - read a document from an image instead of parsing JSON text
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - reusing a document and what it invalidates
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - reusing a document and what it invalidates
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ auto v = doc.root();
|
||||
returned view is valid under the same conditions as any other view of the document — see
|
||||
[Object inspection](../basic_json_view/index.md) — in particular, it is invalidated by the next
|
||||
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document. A view can dangle, like a
|
||||
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view/lifetime.md).
|
||||
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view.md#ownership-and-lifetime).
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ big-endian reader could not interpret correctly.
|
||||
- [`load`](load.md) - read an image written by `save()`
|
||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
|
||||
- [Images](../../features/json_view/images.md) - why and when to use images
|
||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ one-node scalar.
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
|
||||
spelling with `#!cpp number_format::source`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
- [Editing a document](../../features/json_view/editing.md) - why editable documents keep the source
|
||||
- [Editing a document](../../features/json_view.md#editing-a-document) - why editable documents keep the source
|
||||
text's order and number spelling
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -56,7 +56,7 @@ possible would be an internal optimization only, not a guarantee to rely on.
|
||||
- [`node_count`](node_count.md) - the number of index entries
|
||||
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
|
||||
- [`root`](root.md) - the view of the root value
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - what releasing memory invalidates
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - what releasing memory invalidates
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ is; see [`owns_source`](owns_source.md).
|
||||
|
||||
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [`source_offset`](../basic_json_view/source_offset.md) - byte offset of a value in the source text
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ Pointer path of the value within the document, and with
|
||||
|
||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
||||
the last value.
|
||||
|
||||
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
|
||||
|
||||
@@ -58,7 +58,7 @@ placeholder for "no value yet" and later be assigned a real view.
|
||||
- [`is_discarded`](is_discarded.md) - return whether the view is invalid
|
||||
- [`root`](../basic_json_document/root.md) - the view of a document's root value
|
||||
- [`view_mode`](../view_mode.md) - whether a document and its views can be edited
|
||||
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - how long a view stays valid
|
||||
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - how long a view stays valid
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ No-throw guarantee: this function never throws exceptions.
|
||||
|
||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
||||
the last value.
|
||||
|
||||
Overload 1 always returns `#!cpp false` when the value is not an object — including a [discarded](is_discarded.md)
|
||||
|
||||
@@ -70,7 +70,7 @@ except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()
|
||||
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
|
||||
for `#!cpp -0` — `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
|
||||
digits than any number type holds (such a literal is itself classified as a float, see
|
||||
[What is different](../../features/json_view/differences.md)) — something `BasicJsonType` cannot do, since
|
||||
[What is different](../../features/json_view.md#what-is-different)) — something `BasicJsonType` cannot do, since
|
||||
parsing already reduces every number to its parsed value.
|
||||
|
||||
## Examples
|
||||
@@ -98,7 +98,7 @@ parsing already reduces every number to its parsed value.
|
||||
- [`operator<<`](operator_ltlt.md) - serialize this value to a stream
|
||||
- [`materialize`](materialize.md) - build a `BasicJsonType` value, e.g. to use `BasicJsonType::dump`'s `error_handler`
|
||||
- [`BasicJsonType::dump`](../basic_json/dump.md) - the corresponding function of `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
|
||||
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ constant time on average.
|
||||
|
||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
||||
the last value.
|
||||
|
||||
Unlike [`BasicJsonType::find`](../basic_json/find.md), which always returns `#!cpp end()` for a non-object type, this
|
||||
|
||||
@@ -39,14 +39,14 @@ as [`BasicJsonType::parse()`](../basic_json/parse.md) does), as well as the type
|
||||
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<typename BasicJsonType> 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 [Writing generic code](../../features/json_view/generic_code.md).
|
||||
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view.md#writing-generic-code).
|
||||
|
||||
!!! 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<T>()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md)
|
||||
keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get<point>()` reads `#!cpp x == 1`. See
|
||||
[Duplicate keys](../../features/json_view/differences.md#duplicate-keys).
|
||||
[Duplicate keys](../../features/json_view.md#duplicate-keys).
|
||||
|
||||
Every other `T` — user types whose `from_json()` is written for `BasicJsonType` only, types without a default
|
||||
constructor, `#!cpp std::tuple`s holding references, ... — is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
|
||||
@@ -152,7 +152,7 @@ instead: its path is relative to the root of the view's subtree, because `materi
|
||||
- [`number_token`](number_token.md) - a number's token text, without a copy
|
||||
- [`materialize`](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ since floating-point values and unescaped strings are read from the source lazil
|
||||
- [`get`](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
|
||||
- [`number_token`](number_token.md) - a number's token text, without a copy
|
||||
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - borrowing strings instead of copying them
|
||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - borrowing strings instead of copying them
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ them with those of a default-constructed `T`.
|
||||
|
||||
- [`get`](get.md) - convert the value to a given type
|
||||
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ still refers to it — including ones taken before the change — reads the new
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
|
||||
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
|
||||
- [`basic_json_document`](../basic_json_document/index.md) - the document that owns the parsed data
|
||||
- [`json_view`](../json_view.md) - and [`json_editable_view`](../json_editable_view.md) - the default specializations
|
||||
- [`materialize`](materialize.md) - build a `basic_json` value from a view
|
||||
|
||||
@@ -54,7 +54,7 @@ A `basic_json_view` is not convertible to `#!cpp bool`: such a conversion would
|
||||
- [`(constructor)`](basic_json_view.md) - the default constructor creates a discarded view
|
||||
- [`basic_json_document::is_discarded`](../basic_json_document/is_discarded.md) - return whether the last parse failed
|
||||
- [`BasicJsonType::is_discarded`](../basic_json/is_discarded.md) - the corresponding function of `basic_json`
|
||||
- [Zero-copy JSON views](../../features/json_view/differences.md) - how views differ from basic_json
|
||||
- [Zero-copy JSON views](../../features/json_view.md#what-is-different) - how views differ from basic_json
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ never caches the result.
|
||||
- [`root`](../basic_json_document/root.md) - the view of a document's root value
|
||||
- [`BasicJsonType::parse`](../basic_json/parse.md) - build a `BasicJsonType` value directly from a JSON text
|
||||
- [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) - source positions on parsed values
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - when to build a basic_json value from a view
|
||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - when to build a basic_json value from a view
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ source
|
||||
- [`number_token`](number_token.md) - get a single number's token text without dumping the whole value
|
||||
- [`BasicJsonType::error_handler_t`](../basic_json/error_handler_t.md) - the analogous enumeration for
|
||||
`BasicJsonType::dump`'s decoding-error behavior
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing numbers as they appeared in the source
|
||||
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing numbers as they appeared in the source
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ buffer — and is valid exactly as long as that text is, see the [validity rules
|
||||
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
|
||||
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
|
||||
number types `#!cpp get<T>()` and `materialize()` convert into
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - reading a number exactly as written
|
||||
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - reading a number exactly as written
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ document.
|
||||
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
|
||||
The key hash index of a larger object (128 members or more) leads to the first member of a key as well.
|
||||
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
||||
duplicates, in document order. See [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) and
|
||||
duplicates, in document order. See [Duplicate keys](../../features/json_view.md#duplicate-keys) and
|
||||
[`size()`](size.md#notes).
|
||||
|
||||
!!! info "JSON pointer resolution"
|
||||
|
||||
@@ -19,7 +19,7 @@ Neither overload builds a `BasicJsonType` value for a view to do the comparison
|
||||
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
|
||||
resolved exactly as `parse()` resolves them — the last value, at the position of the first occurrence of the key.
|
||||
This is not what [`operator[]`](operator[].md) and the other lookups return for a duplicate key: they find the *first*
|
||||
member (see [Duplicate keys](../../features/json_view/differences.md#duplicate-keys)).
|
||||
member (see [Duplicate keys](../../features/json_view.md#duplicate-keys)).
|
||||
|
||||
## Parameters
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ Linear, as [`dump`](dump.md#complexity).
|
||||
- [`dump`](dump.md) - serialize to a JSON-formatted string
|
||||
- [`operator<<(std::ostream&)`](../operator_ltlt.md) - the corresponding operator for `basic_json`
|
||||
- [`JSON_NO_IO`](../macros/json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
||||
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
|
||||
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ string, whose token length is not the same as its decoded [`size()`](size.md).
|
||||
|
||||
- [`source`](../basic_json_document/source.md) - the parsed text
|
||||
- [`is_string`](is_string.md) - return whether the value is a string
|
||||
- [Zero-copy JSON views](../../features/json_view/images.md) - images and the source text
|
||||
- [Zero-copy JSON views](../../features/json_view.md#images) - images and the source text
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ Pointer path of the value within the document, and with
|
||||
|
||||
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
|
||||
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
||||
the last value.
|
||||
|
||||
!!! info "Differences to `at` and `operator[]`"
|
||||
|
||||
@@ -49,7 +49,7 @@ view class.
|
||||
- [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
|
||||
- [Writing generic code](../features/json_view/generic_code.md) - functions and `from_json()` for views
|
||||
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions and `from_json()` for views
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ specialization.
|
||||
|
||||
- [`json_view`](json_view.md) - a view of a value of a `json_document`
|
||||
- [`ordered_json_document`](ordered_json_document.md) - the corresponding document for `ordered_json`
|
||||
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ This type is an **editable** [`basic_json_document`](basic_json_document/index.m
|
||||
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
|
||||
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
|
||||
parsing, without ever rewriting the source text — see [Edits](basic_json_document/index.md#edits) and
|
||||
[Editing a document](../features/json_view/editing.md).
|
||||
[Editing a document](../features/json_view.md#editing-a-document).
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ still referring to it seeing the change, see [Edits](basic_json_document/index.m
|
||||
- [`json_view`](json_view.md) - the corresponding read-only view
|
||||
- [`ordered_json_editable_view`](ordered_json_editable_view.md) - the corresponding view for
|
||||
`ordered_json_editable_document`
|
||||
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
|
||||
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`j
|
||||
|
||||
- [`json_document`](json_document.md) - the document type this view refers into
|
||||
- [`ordered_json_view`](ordered_json_view.md) - the corresponding view for `ordered_json_document`
|
||||
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ By default, `#!cpp JSON_NO_SIMD` is not defined, and vector instructions are use
|
||||
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance) - how input
|
||||
sources affect parsing performance
|
||||
- [Serialization](../../features/serialization.md) - pretty-printing, UTF-8 handling, and number output
|
||||
- [Zero-copy JSON views](../../features/json_view/index.md) - the zero-copy view, whose parser the macro also affects
|
||||
- [Zero-copy JSON views](../../features/json_view.md) - the zero-copy view, whose parser the macro also affects
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ of object keys, instead of sorting them like [`json_document`](json_document.md)
|
||||
- [`ordered_json_view`](ordered_json_view.md) - a view of a value of an `ordered_json_document`
|
||||
- [`json_document`](json_document.md) - the corresponding document for the default `json` specialization
|
||||
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
|
||||
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ members [`set`](basic_json_document/set.md) added — instead of sorting them li
|
||||
specialization
|
||||
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
|
||||
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
||||
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
|
||||
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
|
||||
- [`ordered_json_editable_document`](ordered_json_editable_document.md) - the document type this view refers into
|
||||
- [`ordered_json_view`](ordered_json_view.md) - the corresponding read-only view
|
||||
- [`json_editable_view`](json_editable_view.md) - the corresponding view for `json_editable_document`
|
||||
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
|
||||
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
|
||||
|
||||
- [`ordered_json_document`](ordered_json_document.md) - the document type this view refers into
|
||||
- [`json_view`](json_view.md) - the corresponding view for `json_document`
|
||||
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
|
||||
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ nor a [`json_editable_view`](json_editable_view.md). Views of the two modes comp
|
||||
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
|
||||
- [json_editable_document](json_editable_document.md) - an editable document of `json`
|
||||
- [(constructor)](basic_json_view/basic_json_view.md) - the conversion from a read-only to an editable view
|
||||
- [Writing generic code](../features/json_view/generic_code.md) - functions that accept views
|
||||
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions that accept views
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* figures: jsonview (features/json_view/index.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
|
||||
/* figures: jsonview (features/json_view.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
|
||||
|
||||
/* one colour per concept, the same in every figure: structure (object/array, views) blue, keys navy, strings green,
|
||||
numbers orange, decoded strings purple, owned edit storage teal, dangling/invalid red */
|
||||
|
||||
@@ -48,7 +48,7 @@ which avoids repeating it:
|
||||
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
|
||||
type is obtained from a subtree that is built on demand with
|
||||
[`materialize()`](../api/basic_json_view/materialize.md). See
|
||||
[Zero-copy JSON views](json_view/reading.md#getting-values-out-without-copying).
|
||||
[Zero-copy JSON views](json_view.md#getting-values-out-without-copying).
|
||||
|
||||
The library already knows how to convert to and from the scalar types and the STL containers (such as
|
||||
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
|
||||
@@ -269,7 +269,7 @@ The conversions above are built in for standard types. To make the same syntax w
|
||||
- [`get_ref`](../api/basic_json/get_ref.md) - / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
|
||||
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
|
||||
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
|
||||
- [Zero-copy JSON views](json_view/index.md) - get values out of a document or a view
|
||||
- [Zero-copy JSON views](json_view.md) - get values out of a document or a view
|
||||
- [`adl_serializer`](../api/adl_serializer/index.md) - the default serializer behind the conversions
|
||||
- [`type`](../api/basic_json/type.md) - query the type of a value
|
||||
- [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) - toggle conversion to tuples of references
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
There are several ways to create a JSON value in memory. This page gives an overview; to read a value from JSON text
|
||||
instead, see [parsing](parsing/index.md). A JSON text can also be parsed into a
|
||||
[document](../api/basic_json_document/index.md) and read through a [view](../api/basic_json_view/index.md) instead of
|
||||
into a value; see [Zero-copy JSON views](json_view/index.md).
|
||||
into a value; see [Zero-copy JSON views](json_view.md).
|
||||
|
||||
## From C++ values
|
||||
|
||||
@@ -122,7 +122,7 @@ string `#!json "42"`.
|
||||
- [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal
|
||||
- [Converting values](conversions.md) - which C++ types can be used
|
||||
- [Parsing](parsing/index.md) - create a value from JSON text
|
||||
- [Zero-copy JSON views](json_view/index.md) - create a document and a view from JSON text instead of a value
|
||||
- [Zero-copy JSON views](json_view.md) - create a document and a view from JSON text instead of a value
|
||||
- [`basic_json`](../api/basic_json/index.md) - the class template and its member functions
|
||||
- [Types](types/index.md) - the JSON value types
|
||||
- [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) - change how a single braced value is interpreted
|
||||
|
||||
@@ -13,10 +13,10 @@ C++ types, and finally serialize it again.
|
||||
[SAX interface](parsing/sax_interface.md), [error handling](parsing/parse_exceptions.md),
|
||||
[large and partial input](parsing/large_and_partial_input.md), and
|
||||
[parsing untrusted input](parsing/untrusted_input.md).
|
||||
- [Zero-copy JSON views](json_view/index.md) — read a JSON text through a flat index instead of building a `json` tree;
|
||||
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
|
||||
strings and numbers stay in the input and are only decoded when needed.
|
||||
- [Editable documents](json_view/editing.md) — modify a parsed document without building a `json` tree.
|
||||
- [Images](json_view/images.md) — save a parsed document and load it again without parsing it.
|
||||
- [Editable documents](json_view.md#editing-a-document) — modify a parsed document without building a `json` tree.
|
||||
- [Images](json_view.md#images) — save a parsed document and load it again without parsing it.
|
||||
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
||||
|
||||
## Accessing and modifying values
|
||||
|
||||
@@ -0,0 +1,589 @@
|
||||
# Zero-copy JSON views
|
||||
|
||||
`#!cpp <nlohmann/json_view.hpp>` adds a read-only, non-owning way to look at a parsed JSON text, as an alternative to
|
||||
building a [`basic_json`](../api/basic_json/index.md) tree with [`parse()`](../api/basic_json/parse.md).
|
||||
|
||||
## The problem
|
||||
|
||||
[`basic_json::parse()`](../api/basic_json/parse.md) builds a tree of `basic_json` values: one allocation for every
|
||||
array and object, and every string copied into its own `std::string`. That is the right trade-off when the program
|
||||
goes on to read and write the value freely, but it does more work than necessary when only a small part of a large
|
||||
JSON text is actually needed, or when the same text is parsed over and over (many small messages, for instance) and
|
||||
most of the resulting tree is thrown away almost immediately.
|
||||
|
||||
## The idea
|
||||
|
||||
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
||||
options, but instead of a tree it builds a flat index of the values it found: one
|
||||
[16-byte entry](../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
||||
order. Integers are converted while parsing and stored in their entry. Floating-point numbers and strings are
|
||||
referenced in place in the source text: a float is converted when it is read, and only strings that contain escape
|
||||
sequences are decoded, into one shared buffer owned by the document.
|
||||
|
||||
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
||||
index. It gives you the read-only, type-inspection part of the `basic_json` interface —
|
||||
[`type()`](../api/basic_json_view/type.md) and the `is_*()` predicates,
|
||||
[`size()`](../api/basic_json_view/size.md)/[`empty()`](../api/basic_json_view/empty.md) — as well as element access
|
||||
([`operator[]`](../api/basic_json_view/operator%5B%5D.md), [`at`](../api/basic_json_view/at.md),
|
||||
[`front`](../api/basic_json_view/front.md)/[`back`](../api/basic_json_view/back.md)), lookup
|
||||
([`find`](../api/basic_json_view/find.md), [`contains`](../api/basic_json_view/contains.md),
|
||||
[`count`](../api/basic_json_view/count.md)), and iteration
|
||||
([`begin`](../api/basic_json_view/begin.md)/[`end`](../api/basic_json_view/end.md),
|
||||
[`items`](../api/basic_json_view/items.md)) — without ever allocating a `basic_json` value. When you do need an
|
||||
actual `basic_json` value for a subtree, [`materialize()`](../api/basic_json_view/materialize.md) builds exactly the
|
||||
one [`parse()`](../api/basic_json/parse.md) would have produced for it.
|
||||
|
||||
The same small text, parsed both ways:
|
||||
|
||||
<div class="nj-fig-wrap" markdown>
|
||||
--8<-- "images/json_view_layout.svg"
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>Top: the tree of separate heap allocations that `basic_json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.</p>
|
||||
|
||||
## How to use it
|
||||
|
||||
Include `<nlohmann/json_view.hpp>` in addition to (or instead of) `<nlohmann/json.hpp>`. Parse into a
|
||||
[`json_document`](../api/json_document.md), inspect its [`root()`](../api/basic_json_document/root.md), and
|
||||
`materialize()` when you need a real value. `materialize()` works on any view, not just the root, so you can turn just
|
||||
the subtree you need into a value:
|
||||
|
||||
??? example "Example: parse a document, inspect its root, and materialize it"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_document.output"
|
||||
```
|
||||
|
||||
[`ordered_json_document`](../api/ordered_json_document.md) is the equivalent for
|
||||
[`ordered_json`](../api/ordered_json.md), just as [`ordered_json`](../api/ordered_json.md) is to
|
||||
[`json`](../api/json.md).
|
||||
|
||||
## Ownership and lifetime
|
||||
|
||||
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call
|
||||
[`owns_source()`](../api/basic_json_document/owns_source.md) to find out which happened.
|
||||
[`parse()`](../api/basic_json_document/parse.md) decides this from the value category and type of its argument (an
|
||||
lvalue `#!cpp std::string` is borrowed; an rvalue `#!cpp std::string` is moved in, owned without a copy; a stream is
|
||||
read into an owned buffer; and so on — see [`parse`'s Notes](../api/basic_json_document/parse.md#notes) for the full
|
||||
table). [`parse_copy()`](../api/basic_json_document/parse_copy.md) always owns a copy, regardless of the input.
|
||||
|
||||
!!! warning "A borrowed document depends on your buffer"
|
||||
|
||||
If a document borrows its text, that text **must outlive the document** (and every view taken from it) and must
|
||||
stay **unmodified** for as long as the document exists, since floating-point values and unescaped strings are
|
||||
read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior,
|
||||
exactly as it would be for a dangling `#!cpp std::string_view`.
|
||||
|
||||
A view is valid only while all of the following hold:
|
||||
|
||||
- the document is alive,
|
||||
- the document has not been re-parsed since the view was taken (with [`read()`](../api/basic_json_document/read.md)
|
||||
or [`parse()`](../api/basic_json_document/parse.md) into it),
|
||||
- the document has not had [`shrink_to_fit()`](../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
||||
- if the document borrows its source text, that text is still alive and unmodified.
|
||||
|
||||
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
||||
allocation that keeps its address across the move. Take a fresh view from [`root()`](../api/basic_json_document/root.md)
|
||||
whenever any of the other conditions above was not met.
|
||||
|
||||
<div class="nj-fig-wrap" markdown>
|
||||
--8<-- "images/json_view_lifetime.svg"
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.</p>
|
||||
|
||||
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
|
||||
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
|
||||
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
|
||||
and a `lock()` to every access — exactly the costs the view exists to avoid. What is caught is the obvious case:
|
||||
because a view dies with its document, [`root()`](../api/basic_json_document/root.md) is not callable on a temporary
|
||||
document: `#!cpp auto v = json_document::parse(text).root();` does not compile. Give the document a name first.
|
||||
|
||||
??? example "Example: borrowed and owned documents, and when views become invalid"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_view_ownership.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_view_ownership.output"
|
||||
```
|
||||
|
||||
## What is the same as `parse()`
|
||||
|
||||
- **Accept/reject.** [`accept()`](../api/basic_json_document/accept.md) and
|
||||
[`parse()`](../api/basic_json_document/parse.md) accept and reject exactly the same inputs as
|
||||
[`basic_json::accept()`](../api/basic_json/accept.md)/[`basic_json::parse()`](../api/basic_json/parse.md), with the
|
||||
same `ignore_comments` and `ignore_trailing_commas` options.
|
||||
- **Errors.** A failing parse throws the same exception — the same id, message, and position — because on failure
|
||||
the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and
|
||||
the replay builds no values: it costs about a lexing pass up to the error.
|
||||
`#!cpp allow_exceptions == false` skips the replay and gives a
|
||||
[discarded](../api/basic_json_document/is_discarded.md) document instead of throwing, just as it gives a discarded
|
||||
value for `#!cpp basic_json::parse()`.
|
||||
- **Number classification.** An integer literal that does not fit into the 64-bit integer type becomes a
|
||||
floating-point value, exactly as it does for `#!cpp basic_json::parse()`.
|
||||
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) and
|
||||
[`JSON_NOEXCEPTION`](../api/macros/json_noexception.md)/[`JSON_THROW_USER`](../api/macros/json_throw_user.md)
|
||||
behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or
|
||||
how it reports errors. Two more macros add context to the exceptions of views, as they do for those of
|
||||
`#!cpp basic_json`:
|
||||
|
||||
- With [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md), an exception names the JSON Pointer path of the
|
||||
value within the document.
|
||||
- With [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md), an exception names the bytes of
|
||||
the value in the source text.
|
||||
|
||||
A view has neither parent pointers nor stored positions, so both are found from the document's index when the
|
||||
exception is thrown. This takes time linear in the size of the document. One exception has neither:
|
||||
`key()` of an iterator over an array or a primitive value
|
||||
([`invalid_iterator.207`](../home/exceptions.md#jsonexceptioninvalid_iterator207)), because a view's iterator does
|
||||
not know the value it iterates over.
|
||||
|
||||
The other macros only affect the values `materialize()` produces, through `BasicJsonType`.
|
||||
|
||||
## What is different
|
||||
|
||||
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and
|
||||
`number_unsigned_t` to both be 64 bits wide; this is a compile-time `#!cpp static_assert`.
|
||||
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws
|
||||
[`out_of_range.416`](../home/exceptions.md#jsonexceptionout_of_range416), a limit
|
||||
`#!cpp basic_json::parse()` does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits
|
||||
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
||||
end must still fit.
|
||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
||||
is read to its end into a buffer the document owns, and parsing starts after that.
|
||||
- **Fewer source positions after edits.** With
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`.
|
||||
In an [editable document](../api/json_editable_document.md), a value written by an edit has none, and neither has
|
||||
an array or object whose elements changed or whose last element has none; a `basic_json` value keeps its positions
|
||||
when it is changed. See [`materialize()`](../api/basic_json_view/materialize.md#notes).
|
||||
- **Objects iterate in document order.** [`begin()`](../api/basic_json_view/begin.md)/
|
||||
[`end()`](../api/basic_json_view/end.md) and [`items()`](../api/basic_json_view/items.md) visit an object's
|
||||
members in the order they appear in the source text. `basic_json`'s default `object_t` is a `std::map`, which
|
||||
sorts by key, so iterating a [`materialize()`](../api/basic_json_view/materialize.md)d value can print members in
|
||||
a different order than iterating the view they came from.
|
||||
- **Chained access is safe.** [`operator[]`](../api/basic_json_view/operator%5B%5D.md) with a missing key, an index
|
||||
out of range, or an unresolvable JSON pointer returns a [discarded](../api/basic_json_view/is_discarded.md) view, and
|
||||
`operator[]` on a discarded view returns a discarded view without throwing: `#!cpp v["a"]["b"][0]` can be tested
|
||||
once at the end. Type errors on values that exist (a key on an array, an index on an object) still throw, and
|
||||
[`at`](../api/basic_json_view/at.md) throws for every missing value.
|
||||
- **Duplicate keys: lookups find the first member, `parse()` keeps the last.** See [Duplicate keys](#duplicate-keys)
|
||||
below.
|
||||
- **Ordering comparisons are not provided** by `basic_json_view` — there is no `#!cpp operator<`.
|
||||
[`operator==`](../api/basic_json_view/operator_eq.md) and [`operator!=`](../api/basic_json_view/operator_ne.md) are
|
||||
provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) or [`parse()`](../api/basic_json/parse.md) would produce
|
||||
equal values for them, without ever building a tree to do it. For ordering, too,
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
||||
|
||||
## Duplicate keys
|
||||
|
||||
!!! warning "A lookup in a view and in the parsed value can give different answers"
|
||||
|
||||
If an object in the source text repeats a key, [`operator[]`](../api/basic_json_view/operator%5B%5D.md),
|
||||
[`at`](../api/basic_json_view/at.md), [`find`](../api/basic_json_view/find.md),
|
||||
[`value`](../api/basic_json_view/value.md), [`contains`](../api/basic_json_view/contains.md), and JSON pointer
|
||||
resolution all return the **first** member with that key. `basic_json::parse()` instead keeps the **last** value
|
||||
of a repeated key, so for `#!cpp {"a":1,"a":2}`, `#!cpp view["a"]` is `#!cpp 1` while `#!cpp parse(text)["a"]` is
|
||||
`#!cpp 2`. [`count`](../api/basic_json_view/count.md) counts every member with the key, so it is `#!cpp 2` here.
|
||||
|
||||
[`begin()`](../api/basic_json_view/begin.md)/[`end()`](../api/basic_json_view/end.md) and
|
||||
[`items()`](../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
||||
[`size()`](../api/basic_json_view/size.md) counts all of them, so `#!cpp view.size()` can be larger than
|
||||
`#!cpp view.materialize().size()`.
|
||||
|
||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
||||
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
||||
objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first member of a
|
||||
key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return the first
|
||||
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<T>()`](../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<point>()` of
|
||||
`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get<point>()` reads `#!cpp 2`.
|
||||
|
||||
**What stays the same as `parse()`?** [`materialize()`](../api/basic_json_view/materialize.md) and
|
||||
[`get<std::map<...>>()`](../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
|
||||
[`ordered_json`](../api/ordered_json.md)).
|
||||
|
||||
**How do I get the value `parse()` would give?** Either call `#!cpp view.materialize()` and look the key up in the
|
||||
result, or iterate the members and keep the last match:
|
||||
|
||||
```cpp
|
||||
// the last member with the key "a", as parse() would keep it
|
||||
json_view last;
|
||||
for (const auto item : view.items())
|
||||
{
|
||||
if (item.key() == "a")
|
||||
{
|
||||
last = item.value();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you cannot trust the source text to have unique keys, check [`size()`](../api/basic_json_view/size.md) against the
|
||||
number of distinct keys, or reject duplicates when iterating.
|
||||
|
||||
## Getting values out without copying
|
||||
|
||||
[`get<T>()`](../api/basic_json_view/get.md) converts most `T` directly from the flat index, without ever building a
|
||||
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
|
||||
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json_view` itself, the standard containers
|
||||
and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::list`, `#!cpp std::array`,
|
||||
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, ...), and the types of the `NLOHMANN_DEFINE_TYPE_*` and
|
||||
`NLOHMANN_JSON_SERIALIZE_ENUM` macros. Types whose `from_json()` is written for `basic_json` only go through
|
||||
[`materialize()`](../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
|
||||
`basic_json` value first, exactly as [`parse()`](../api/basic_json/parse.md) would, and converted from there. See
|
||||
[Writing generic code](#writing-generic-code) for how to write a `from_json()` that reads views.
|
||||
|
||||
Two conversions never copy at all:
|
||||
|
||||
- [`get_string()`](../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
||||
string as a `string_view_t` pointing into the document's [`source()`](../api/basic_json_document/source.md) text —
|
||||
or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of
|
||||
allocating a new `#!cpp std::string`.
|
||||
- [`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
||||
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
||||
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
||||
[`basic_json::parse()`](../api/basic_json/parse.md)) would.
|
||||
|
||||
Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.
|
||||
|
||||
### `string_view` members
|
||||
|
||||
A struct whose member is a `#!cpp std::string_view` (C++17) can be decoded from a view with the
|
||||
`NLOHMANN_DEFINE_TYPE_*` macros, and the member is not copied either: for a string without escape sequences it points
|
||||
into the document's source text, and for a string with escape sequences into the document's buffer of decoded strings.
|
||||
The member is valid as long as the document is alive and, for a document that
|
||||
[borrows its source text](#ownership-and-lifetime) (as [`parse()`](../api/basic_json_document/parse.md) does for
|
||||
an lvalue `#!cpp std::string`, a `#!cpp std::string_view`, or a C string), as long as that text is.
|
||||
|
||||
```cpp
|
||||
struct record
|
||||
{
|
||||
std::string_view name;
|
||||
int count;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||
};
|
||||
|
||||
const std::string text = R"({"name": "widget", "count": 3})";
|
||||
const json_document doc = json_document::parse(text); // borrows text
|
||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||
```
|
||||
|
||||
!!! warning "Decoding into a `string_view` from a `json` value dangles at once"
|
||||
|
||||
The same struct also compiles with `#!cpp json::parse(text).get<record>()`, but there `name` points into the
|
||||
temporary `json` value, which is destroyed at the end of the statement. Use a named
|
||||
[`json_document`](../api/json_document.md), as above, to decode such a struct.
|
||||
|
||||
## Writing generic code
|
||||
|
||||
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.
|
||||
|
||||
One thing differs when a `from_json()` reads the view: members looked up by name (`at()`, `value()`, ... and thus
|
||||
the macros) find the **first** member with a repeated key, see [Duplicate keys](#duplicate-keys);
|
||||
`#!cpp materialize().get<T>()` would see the last one. Containers built from an object, such as `#!cpp std::map`,
|
||||
keep the **last** value either way, as [`parse()`](../api/basic_json/parse.md) does.
|
||||
|
||||
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
||||
|
||||
## Writing a view back
|
||||
|
||||
[`dump()`](../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
|
||||
`basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a
|
||||
repeated key is written, not only the last one — the same two ways [iteration](#what-is-different) already differs
|
||||
from a [`materialize()`](../api/basic_json_view/materialize.md)d value, see [Duplicate keys](#duplicate-keys).
|
||||
`#!cpp materialize().dump()` gives a different result in both respects for a `json_view`.
|
||||
|
||||
By default, numbers are written the way [`basic_json::dump()`](../api/basic_json/dump.md) would.
|
||||
[`number_format::source`](../api/basic_json_view/number_format.md) instead copies every number exactly as it was
|
||||
written in the source text — a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
|
||||
type holds, or a high-precision coordinate — something `basic_json` cannot do at all, since parsing already reduces
|
||||
a number to its parsed `#!cpp double`/`#!cpp int64_t` value.
|
||||
|
||||
[`operator<<`](../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
|
||||
the stream's `width`/`fill` for indentation.
|
||||
|
||||
## Editing a document
|
||||
|
||||
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but
|
||||
not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](../api/basic_json_document/index.md) — more conveniently
|
||||
spelled [`json_editable_document`](../api/json_editable_document.md) or
|
||||
[`ordered_json_editable_document`](../api/ordered_json_editable_document.md) — also lets you
|
||||
[`set`](../api/basic_json_document/set.md) a value, [`push_back`](../api/basic_json_document/push_back.md) onto or
|
||||
[`insert`](../api/basic_json_document/insert.md) into an array, and [`erase`](../api/basic_json_document/erase.md)
|
||||
an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
|
||||
|
||||
The [mode](../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected — they carry
|
||||
none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a
|
||||
runtime one.
|
||||
|
||||
### Why: editing without reformatting
|
||||
|
||||
The `#!cpp 19.90` price from [above](#writing-a-view-back) is exactly the kind of value that makes editing a `json`
|
||||
or `ordered_json` value in place lossy. Say you parse a configuration file, patch one field, and write it back:
|
||||
|
||||
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
||||
shortest round-trip form on the way out — a one-field patch turns into a diff that reorders the whole file and
|
||||
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
||||
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
||||
|
||||
The patch of the example below (set `"host"`, add `"retries"`) written back by each of them:
|
||||
|
||||
<div class="nj-jsonview-cmp" role="group" aria-label="The same patch written back by json, ordered_json, and json_editable_document">
|
||||
<div><p class="nj-jsonview-cmp-h"><code>json</code></p><p class="nj-jsonview-cmp-v">keys sorted, numbers rewritten</p><pre>{<span class="nj-jsonview-bad">"host"</span>:"db2",
|
||||
"name":"cache",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||
"retries":3}</pre></div>
|
||||
<div><p class="nj-jsonview-cmp-h"><code>ordered_json</code></p><p class="nj-jsonview-cmp-v">key order kept, numbers rewritten</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||
"retries":3}</pre></div>
|
||||
<div class="nj-jsonview-cmp--best"><p class="nj-jsonview-cmp-h"><code>json_editable_document</code></p><p class="nj-jsonview-cmp-v">order and spelling kept</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-ok">19.90</span>,
|
||||
"retries":3}</pre></div>
|
||||
</div>
|
||||
|
||||
An editable document keeps both. [`dump()`](../api/basic_json_view/dump.md) of an edited document writes members in
|
||||
document order — a member [`set`](../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
||||
inserted, and an [`erase`](../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
||||
keeps its place — and [`number_format::source`](../api/basic_json_view/number_format.md) keeps the exact spelling
|
||||
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
||||
[`BasicJsonType::dump()`](../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
||||
new value.
|
||||
|
||||
??? example "Example: patch a configuration, keeping member order and number spellings"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
### What stays valid, and what an edit costs
|
||||
|
||||
The source text itself is **never written**, and the parsed index never moves — a value keeps the node it was
|
||||
parsed into for as long as it is not itself replaced. So every [view](../api/basic_json_view/index.md) taken before
|
||||
an edit, including a previously obtained [`root()`](../api/basic_json_document/root.md), stays valid and, if it
|
||||
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
||||
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
||||
invalidate is the **iterators** taken over an edited array or object: the first time an element is added to it,
|
||||
inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move from the parsed, fixed layout to a growable block of links
|
||||
so that [`push_back`](../api/basic_json_document/push_back.md) can later grow it in amortized constant time —
|
||||
existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string
|
||||
obtained with
|
||||
[`get_string()`](../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
||||
edits. See [`basic_json_document`'s Edits](../api/basic_json_document/index.md#edits) for the details, and
|
||||
[`set`'s Exception safety](../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
||||
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
||||
the index is described in the [architecture overview](../home/architecture.md#node-index-of-json-views).
|
||||
|
||||
Step through the edits of a small document (the node fields follow the [architecture overview](../home/architecture.md#node-index-of-json-views)):
|
||||
|
||||
<div class="nj-jsonview-steps" data-nj-jsonview-steps>
|
||||
<figure class="nj-jsonview-step" data-label="After parsing">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_0.svg"
|
||||
</div>
|
||||
<figcaption>Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="set an existing member">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_1.svg"
|
||||
</div>
|
||||
<figcaption>Step 2 of 5. <code>doc.set(doc.root(), "port", 6380)</code> overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="erase an array element">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_2.svg"
|
||||
</div>
|
||||
<figcaption>Step 3 of 5. <code>doc.erase(doc.root()["tags"], 0)</code>: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of <code>"eu"</code> stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="push_back onto the array">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_3.svg"
|
||||
</div>
|
||||
<figcaption>Step 4 of 5. <code>doc.push_back(doc.root()["tags"], "new")</code>: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="set a new member">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_4.svg"
|
||||
</div>
|
||||
<figcaption>Step 5 of 5. <code>doc.set(doc.root(), "retries", 3)</code>: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.</p>
|
||||
|
||||
## Images
|
||||
|
||||
[`save()`](../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
|
||||
[`load()`](../api/basic_json_document/load.md) reads back into a document without parsing — no lexing, no building
|
||||
the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where
|
||||
[`parse_copy()`](../api/basic_json_document/parse_copy.md) still has to scan the whole input,
|
||||
[`load()`](../api/basic_json_document/load.md) turns that scan into a copy of the node index alone.
|
||||
|
||||
**Why.** A document that is parsed once and then read many times — a configuration loaded at startup, a template
|
||||
rendered on every request, a large reference dataset a worker process needs in memory — pays for parsing once but
|
||||
can amortize [`save()`](../api/basic_json_document/save.md)'s cost across every later load. That makes images useful
|
||||
for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache),
|
||||
and [`load()`](../api/basic_json_document/load.md) it on every later use instead of parsing the source text again.
|
||||
They are just as useful for handing a parsed document to another process (or a forked worker) running the same build
|
||||
of the library, since [`load()`](../api/basic_json_document/load.md) turns the transfer into a copy of the node index
|
||||
plus pointers into the received bytes, not a re-parse.
|
||||
|
||||
**Choosing a check.** [`load()`](../api/basic_json_document/load.md) takes an
|
||||
[`image_check`](../api/basic_json_document/load.md#image_check) that trades validation against speed:
|
||||
`image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as
|
||||
safe to read and serialize as a freshly parsed document — the right choice whenever the image did not come straight
|
||||
from this process's own [`save()`](../api/basic_json_document/save.md), such as a file or a network peer.
|
||||
`image_check::bounds` only checks structure and bounds — cheaper, since it skips scanning the text and the decoded
|
||||
strings — and fits a cache the process trusts, one it wrote and reads back itself. `image_check::none` skips
|
||||
validation entirely, for an image trusted as much as the process's own memory. See
|
||||
[`load()`'s Notes](../api/basic_json_document/load.md#notes) for exactly what each level does and does not guarantee.
|
||||
|
||||
??? example "Example: cache a parsed configuration as an image"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__save.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__save.output"
|
||||
```
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||
stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the
|
||||
library build that wrote it — use it to cache a document or to hand one to another process running the *same*
|
||||
build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by
|
||||
a future library version.
|
||||
|
||||
The idea of a document you can read without parsing comes from zero-copy formats such as
|
||||
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
||||
[`load()`](../api/basic_json_document/load.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from
|
||||
either.
|
||||
|
||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||
|
||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) | [`json_editable_document`](../api/json_editable_document.md) / [`json_editable_view`](../api/json_editable_view.md) |
|
||||
|---|---|---|---|---|
|
||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
|
||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../api/basic_json_document/set.md)/[`push_back`](../api/basic_json_document/push_back.md)/[`insert`](../api/basic_json_document/insert.md)/[`erase`](../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
|
||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../api/basic_json_view/number_format.md), the spelling of every untouched number |
|
||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was |
|
||||
| **Caching/reload** | not applicable — re-parse, or roll your own serialization | not applicable | [`save()`](../api/basic_json_document/save.md)/[`load()`](../api/basic_json_document/load.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current — possibly edited — state |
|
||||
|
||||
<p class="nj-table-caption" markdown>Each column is one way to work with JSON in this library, and each row compares one property of them.</p>
|
||||
|
||||
## See also
|
||||
|
||||
- [`basic_json_document`](../api/basic_json_document/index.md) - the document that owns the parsed index (parse, access, images, edits)
|
||||
- [`basic_json_view`](../api/basic_json_view/index.md) - the non-owning handle to a value in a document
|
||||
- [`json_document`](../api/json_document.md) - and [`ordered_json_document`](../api/ordered_json_document.md) - the read-only documents
|
||||
- [`json_editable_document`](../api/json_editable_document.md) - and [`ordered_json_editable_document`](../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
||||
- [Architecture](../home/architecture.md#node-index-of-json-views) - the layout of the node index
|
||||
- [SAX interface](parsing/sax_interface.md) - event-based parsing without building a tree
|
||||
- [Performance](performance.md) - benchmarks and tuning of parsing and serialization
|
||||
- [Large, Streamed, and Partial Input](parsing/large_and_partial_input.md) - reading only the part of a text you need
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,219 +0,0 @@
|
||||
# Differences from `basic_json`
|
||||
|
||||
A [`basic_json_view`](../../api/basic_json_view/index.md) offers the read-only part of the
|
||||
[`basic_json`](../../api/basic_json/index.md) interface, and in most respects it behaves exactly like the value
|
||||
[`basic_json::parse()`](../../api/basic_json/parse.md) would have built from the same text. This page lists where it
|
||||
does not: what a document parses differently, what a view's functions do differently, and which parts of the
|
||||
`basic_json` interface a view does not have.
|
||||
|
||||
## Summary
|
||||
|
||||
| | `basic_json` value from `parse()` | view of a `json_document` | to get the `basic_json` behavior |
|
||||
|---|---|---|---|
|
||||
| [Integer types](#parsing) | any `number_integer_t`/`number_unsigned_t` | 64-bit only (compile-time check) | — |
|
||||
| [Input size](#parsing) | unlimited | less than 4 GiB | — |
|
||||
| [Parser callbacks](#parsing) | `parser_callback_t` | none | filter while iterating |
|
||||
| [`operator[]` on a missing key or index](#element-access) | inserts (non-`const`) or undefined behavior (`const`) | returns a discarded view | — |
|
||||
| [Object member order](#object-member-order) | sorted by key (`json`), insertion order (`ordered_json`) | document order | `materialize()` |
|
||||
| [Repeated keys](#duplicate-keys) | the last value is kept | lookups find the first member; iteration, `size()`, and `dump()` see all of them | `materialize()` |
|
||||
| [Conversions](#conversions) | `get<T>()`, implicit conversions, `get_ptr()`, `get_ref()` | `get<T>()`, `get_string()`, `number_token()` | `materialize()` |
|
||||
| [Number spelling](#serialization) | lost when parsing | kept: `number_token()`, `number_format::source` | — |
|
||||
| [Comparison](#comparison) | `==`, `!=`, `<`, `<=>`, ... | `==` and `!=` only | `materialize()` |
|
||||
| [Modification](#what-a-view-does-not-have) | member functions of the value | only through an [editable document](editing.md) | `materialize()` |
|
||||
| [Iterators](#what-a-view-does-not-have) | bidirectional, reverse iterators | forward only | `materialize()` |
|
||||
|
||||
<p class="nj-table-caption" markdown>Each row is one difference; the last column names the way to get `basic_json`'s behavior from a view, where there is one.</p>
|
||||
|
||||
## Parsing
|
||||
|
||||
### What is the same
|
||||
|
||||
- **Accept/reject.** [`accept()`](../../api/basic_json_document/accept.md) and
|
||||
[`parse()`](../../api/basic_json_document/parse.md) accept and reject exactly the same inputs as
|
||||
[`basic_json::accept()`](../../api/basic_json/accept.md)/[`basic_json::parse()`](../../api/basic_json/parse.md),
|
||||
with the same `ignore_comments` and `ignore_trailing_commas` options.
|
||||
- **Errors.** A failing parse throws the same exception — the same id, message, and position — because on failure
|
||||
the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and
|
||||
the replay builds no values: it costs about a lexing pass up to the error.
|
||||
`#!cpp allow_exceptions == false` skips the replay and gives a
|
||||
[discarded](../../api/basic_json_document/is_discarded.md) document instead of throwing, just as it gives a
|
||||
discarded value for `#!cpp basic_json::parse()`.
|
||||
- **Number classification.** An integer literal that does not fit into the 64-bit integer type becomes a
|
||||
floating-point value, exactly as it does for `#!cpp basic_json::parse()`.
|
||||
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](../../api/macros/json_strict_nul_handling.md) and
|
||||
[`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md)/[`JSON_THROW_USER`](../../api/macros/json_throw_user.md)
|
||||
behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or
|
||||
how it reports errors. The other macros only affect the values `materialize()` produces, through `BasicJsonType`.
|
||||
Two of them also add context to the exceptions of views, see [Exception context](#exception-context).
|
||||
|
||||
### What is different
|
||||
|
||||
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and
|
||||
`number_unsigned_t` to both be 64 bits wide; this is a compile-time `#!cpp static_assert`.
|
||||
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws
|
||||
[`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416), a limit
|
||||
`#!cpp basic_json::parse()` does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits
|
||||
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
||||
end must still fit.
|
||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
||||
is read to its end into a buffer the document owns, and parsing starts after that.
|
||||
- **No parser callbacks.** [`parse()`](../../api/basic_json_document/parse.md) takes no
|
||||
[`parser_callback_t`](../../api/basic_json/parser_callback_t.md): a document always indexes the whole text. Skip
|
||||
the values you do not need when reading the view instead.
|
||||
- **JSON text only.** A document is parsed from JSON text; there is no counterpart of
|
||||
[`from_cbor()`](../../api/basic_json/from_cbor.md) and the other binary formats, and
|
||||
[`is_binary()`](../../api/basic_json_view/is_binary.md) is always `#!cpp false`.
|
||||
|
||||
## Element access
|
||||
|
||||
**Chained access is safe.** [`operator[]`](../../api/basic_json_view/operator%5B%5D.md) with a missing key, an index
|
||||
out of range, or an unresolvable JSON pointer returns a [discarded](../../api/basic_json_view/is_discarded.md) view,
|
||||
and `operator[]` on a discarded view returns a discarded view without throwing: `#!cpp v["a"]["b"][0]` can be tested
|
||||
once at the end. Type errors on values that exist (a key on an array, an index on an object) still throw, and
|
||||
[`at`](../../api/basic_json_view/at.md) throws for every missing value.
|
||||
|
||||
`basic_json`'s non-`const` `operator[]` instead inserts a missing key, and its `const` `operator[]` has undefined
|
||||
behavior for one.
|
||||
|
||||
## Objects
|
||||
|
||||
### Object member order
|
||||
|
||||
[`begin()`](../../api/basic_json_view/begin.md)/[`end()`](../../api/basic_json_view/end.md) and
|
||||
[`items()`](../../api/basic_json_view/items.md) visit an object's members in the order they appear in the source text.
|
||||
`basic_json`'s default `object_t` is a `std::map`, which sorts by key, so iterating a
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md)d value can print members in a different order than
|
||||
iterating the view they came from. [`dump()`](../../api/basic_json_view/dump.md) of a view writes members in document
|
||||
order as well.
|
||||
|
||||
### Duplicate keys
|
||||
|
||||
!!! warning "A lookup in a view and in the parsed value can give different answers"
|
||||
|
||||
If an object in the source text repeats a key, [`operator[]`](../../api/basic_json_view/operator%5B%5D.md),
|
||||
[`at`](../../api/basic_json_view/at.md), [`find`](../../api/basic_json_view/find.md),
|
||||
[`value`](../../api/basic_json_view/value.md), [`contains`](../../api/basic_json_view/contains.md), and JSON
|
||||
pointer resolution all return the **first** member with that key. `basic_json::parse()` instead keeps the **last**
|
||||
value of a repeated key, so for `#!cpp {"a":1,"a":2}`, `#!cpp view["a"]` is `#!cpp 1` while
|
||||
`#!cpp parse(text)["a"]` is `#!cpp 2`. [`count`](../../api/basic_json_view/count.md) counts every member with the
|
||||
key, so it is `#!cpp 2` here.
|
||||
|
||||
[`begin()`](../../api/basic_json_view/begin.md)/[`end()`](../../api/basic_json_view/end.md) and
|
||||
[`items()`](../../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
||||
[`size()`](../../api/basic_json_view/size.md) counts all of them, so `#!cpp view.size()` can be larger than
|
||||
`#!cpp view.materialize().size()`. [`dump()`](../../api/basic_json_view/dump.md) writes every occurrence, too, so
|
||||
`#!cpp materialize().dump()` gives a different result for such an object.
|
||||
|
||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
||||
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
||||
objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first
|
||||
member of a key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return
|
||||
the first 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<T>()`](../../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<point>()` of
|
||||
`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get<point>()` reads `#!cpp 2`.
|
||||
|
||||
**What stays the same as `parse()`?** [`materialize()`](../../api/basic_json_view/materialize.md) and
|
||||
[`get<std::map<...>>()`](../../api/basic_json_view/get.md) (and the other containers built from an object) 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 [`ordered_json`](../../api/ordered_json.md)).
|
||||
|
||||
**How do I get the value `parse()` would give?** Either call `#!cpp view.materialize()` and look the key up in the
|
||||
result, or iterate the members and keep the last match:
|
||||
|
||||
```cpp
|
||||
// the last member with the key "a", as parse() would keep it
|
||||
json_view last;
|
||||
for (const auto item : view.items())
|
||||
{
|
||||
if (item.key() == "a")
|
||||
{
|
||||
last = item.value();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you cannot trust the source text to have unique keys, check [`size()`](../../api/basic_json_view/size.md) against
|
||||
the number of distinct keys, or reject duplicates when iterating.
|
||||
|
||||
## Conversions
|
||||
|
||||
- **No implicit conversions.** A view has no `#!cpp operator ValueType()`, no `#!cpp get_ptr()`, and no
|
||||
`#!cpp get_ref()`; use [`get<T>()`](../../api/basic_json_view/get.md), or
|
||||
[`get_string()`](../../api/basic_json_view/get_string.md) for a string without a copy.
|
||||
- **`from_json()` overloads.** [`get<T>()`](../../api/basic_json_view/get.md) calls `T`'s `from_json()` with the view
|
||||
only if that `from_json()` accepts views, and with [`materialize()`](../../api/basic_json_view/materialize.md)
|
||||
otherwise; see [A `from_json()` that reads views](generic_code.md#a-from_json-that-reads-views). Both give the same
|
||||
result except for [repeated keys](#duplicate-keys).
|
||||
- **`string_view` members.** A struct with a `#!cpp std::string_view` member decoded from a view points into the
|
||||
document, see [`string_view` members](reading.md#string_view-members). Decoding the same struct from a `json` value
|
||||
compiles, too, but dangles at once if the value is a temporary.
|
||||
|
||||
## Serialization
|
||||
|
||||
[`dump()`](../../api/basic_json_view/dump.md) writes numbers the way
|
||||
[`basic_json::dump()`](../../api/basic_json/dump.md) would by default, but writes object members in
|
||||
[document order](#object-member-order) and writes [every occurrence of a repeated key](#duplicate-keys).
|
||||
|
||||
A view can also do what a `basic_json` value cannot: [`number_token()`](../../api/basic_json_view/number_token.md)
|
||||
and [`number_format::source`](../../api/basic_json_view/number_format.md) give every number exactly as it was spelled
|
||||
in the source text, while parsing into `basic_json` already reduces a number to its `#!cpp double`/`#!cpp int64_t`
|
||||
value. See [Writing a view back](reading.md#writing-a-view-back).
|
||||
|
||||
## Comparison
|
||||
|
||||
**Ordering comparisons are not provided** by `basic_json_view` — there is no `#!cpp operator<`.
|
||||
[`operator==`](../../api/basic_json_view/operator_eq.md) and [`operator!=`](../../api/basic_json_view/operator_ne.md)
|
||||
are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) or [`parse()`](../../api/basic_json/parse.md) would produce
|
||||
equal values for them, without ever building a tree to do it. For ordering,
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
||||
|
||||
## Exception context
|
||||
|
||||
With [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md), an exception of a view names the JSON Pointer path of
|
||||
the value within the document, and with
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md), it names the bytes of the value in the
|
||||
source text, as they do for the exceptions of `#!cpp basic_json`.
|
||||
|
||||
A view has neither parent pointers nor stored positions, so both are found from the document's index when the
|
||||
exception is thrown. This takes time linear in the size of the document. One exception has neither: `key()` of an
|
||||
iterator over an array or a primitive value
|
||||
([`invalid_iterator.207`](../../home/exceptions.md#jsonexceptioninvalid_iterator207)), because a view's iterator does
|
||||
not know the value it iterates over.
|
||||
|
||||
**Fewer source positions after edits.** With
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) enabled,
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`. In
|
||||
an [editable document](editing.md), a value written by an edit has none, and neither has an array or object whose
|
||||
elements changed or whose last element has none; a `basic_json` value keeps its positions when it is changed. See
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md#notes).
|
||||
|
||||
## What a view does not have
|
||||
|
||||
A view is a read-only handle. These parts of the `basic_json` interface have no counterpart on a view;
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) the subtree to use them:
|
||||
|
||||
- **Modifiers** such as `push_back`, `insert`, `erase`, `update`, or `operator=`. A view never changes a value
|
||||
itself; an [editable document](editing.md) changes the values its views refer to through
|
||||
[`set`](../../api/basic_json_document/set.md), [`push_back`](../../api/basic_json_document/push_back.md),
|
||||
[`insert`](../../api/basic_json_document/insert.md), and [`erase`](../../api/basic_json_document/erase.md).
|
||||
- **Reverse iteration.** A view's iterator is a forward iterator; there is no `rbegin()`/`rend()`.
|
||||
- **Ordering comparisons**, see [Comparison](#comparison).
|
||||
- **JSON Patch and friends**: [`patch()`](../../api/basic_json/patch.md),
|
||||
[`merge_patch()`](../../api/basic_json/merge_patch.md), [`diff()`](../../api/basic_json/diff.md),
|
||||
[`flatten()`](../../api/basic_json/flatten.md).
|
||||
- **Binary formats**: [`to_cbor()`](../../api/basic_json/to_cbor.md) and the other writers, and binary values
|
||||
([`get_binary()`](../../api/basic_json/get_binary.md)).
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`basic_json_view`](../../api/basic_json_view/index.md) - the full interface of a view
|
||||
- [`materialize()`](../../api/basic_json_view/materialize.md) - build the `basic_json` value of a view
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,133 +0,0 @@
|
||||
# Editing a document
|
||||
|
||||
A [`json_document`](../../api/json_document.md)/[`json_view`](../../api/json_view.md) lets you look at a parsed text
|
||||
without copying it, but not change it.
|
||||
[`basic_json_document<BasicJsonType, view_mode::editable>`](../../api/basic_json_document/index.md) — more
|
||||
conveniently spelled [`json_editable_document`](../../api/json_editable_document.md) or
|
||||
[`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) — also lets you
|
||||
[`set`](../../api/basic_json_document/set.md) a value, [`push_back`](../../api/basic_json_document/push_back.md) onto
|
||||
or [`insert`](../../api/basic_json_document/insert.md) into an array, and
|
||||
[`erase`](../../api/basic_json_document/erase.md) an object member or an array element, still without ever building a
|
||||
`basic_json` tree for parts you do not touch.
|
||||
|
||||
The [mode](../../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so
|
||||
`json_document`/`ordered_json_document` are unaffected — they carry none of the bookkeeping edits need, and calling
|
||||
`set`/`push_back`/`insert`/`erase` on one is a compile error, not a runtime one.
|
||||
|
||||
## Why: editing without reformatting
|
||||
|
||||
A price like `#!cpp 19.90` is exactly the kind of value that makes editing a `json` or `ordered_json` value in place
|
||||
lossy. Say you parse a configuration file, patch one field, and write it back:
|
||||
|
||||
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
||||
shortest round-trip form on the way out — a one-field patch turns into a diff that reorders the whole file and
|
||||
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
||||
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
||||
|
||||
The patch of the example below (set `"host"`, add `"retries"`) written back by each of them:
|
||||
|
||||
<div class="nj-jsonview-cmp" role="group" aria-label="The same patch written back by json, ordered_json, and json_editable_document">
|
||||
<div><p class="nj-jsonview-cmp-h"><code>json</code></p><p class="nj-jsonview-cmp-v">keys sorted, numbers rewritten</p><pre>{<span class="nj-jsonview-bad">"host"</span>:"db2",
|
||||
"name":"cache",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||
"retries":3}</pre></div>
|
||||
<div><p class="nj-jsonview-cmp-h"><code>ordered_json</code></p><p class="nj-jsonview-cmp-v">key order kept, numbers rewritten</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||
"retries":3}</pre></div>
|
||||
<div class="nj-jsonview-cmp--best"><p class="nj-jsonview-cmp-h"><code>json_editable_document</code></p><p class="nj-jsonview-cmp-v">order and spelling kept</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-ok">19.90</span>,
|
||||
"retries":3}</pre></div>
|
||||
</div>
|
||||
|
||||
An editable document keeps both. [`dump()`](../../api/basic_json_view/dump.md) of an edited document writes members in
|
||||
document order — a member [`set`](../../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
||||
inserted, and an [`erase`](../../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
||||
keeps its place — and [`number_format::source`](../../api/basic_json_view/number_format.md) keeps the exact spelling
|
||||
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
||||
[`BasicJsonType::dump()`](../../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
||||
new value. See [Writing a view back](reading.md#writing-a-view-back).
|
||||
|
||||
??? example "Example: patch a configuration, keeping member order and number spellings"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
## What stays valid, and what an edit costs
|
||||
|
||||
The source text itself is **never written**, and the parsed index never moves — a value keeps the node it was
|
||||
parsed into for as long as it is not itself replaced. So every [view](../../api/basic_json_view/index.md) taken before
|
||||
an edit, including a previously obtained [`root()`](../../api/basic_json_document/root.md), stays valid and, if it
|
||||
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
||||
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
||||
invalidate is the **iterators** taken over an edited array or object: the first time an element is added to it,
|
||||
inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move
|
||||
from the parsed, fixed layout to a growable block of links so that
|
||||
[`push_back`](../../api/basic_json_document/push_back.md) can later grow it in amortized constant time — existing
|
||||
elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with
|
||||
[`get_string()`](../../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
||||
edits. See [`basic_json_document`'s Edits](../../api/basic_json_document/index.md#edits) for the details, and
|
||||
[`set`'s Exception safety](../../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
||||
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
||||
the index is described in the [architecture overview](../../home/architecture.md#node-index-of-json-views).
|
||||
|
||||
Step through the edits of a small document (the node fields follow the
|
||||
[architecture overview](../../home/architecture.md#node-index-of-json-views)):
|
||||
|
||||
<div class="nj-jsonview-steps" data-nj-jsonview-steps>
|
||||
<figure class="nj-jsonview-step" data-label="After parsing">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_0.svg"
|
||||
</div>
|
||||
<figcaption>Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="set an existing member">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_1.svg"
|
||||
</div>
|
||||
<figcaption>Step 2 of 5. <code>doc.set(doc.root(), "port", 6380)</code> overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="erase an array element">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_2.svg"
|
||||
</div>
|
||||
<figcaption>Step 3 of 5. <code>doc.erase(doc.root()["tags"], 0)</code>: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of <code>"eu"</code> stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="push_back onto the array">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_3.svg"
|
||||
</div>
|
||||
<figcaption>Step 4 of 5. <code>doc.push_back(doc.root()["tags"], "new")</code>: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="set a new member">
|
||||
<div class="nj-jsonview-scroll">
|
||||
--8<-- "images/json_view_edit_4.svg"
|
||||
</div>
|
||||
<figcaption>Step 5 of 5. <code>doc.set(doc.root(), "retries", 3)</code>: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.</p>
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`json_editable_document`](../../api/json_editable_document.md) - and [`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
||||
- [`basic_json_document`'s Edits](../../api/basic_json_document/index.md#edits) - the rules of edits in detail
|
||||
- [Differences from `basic_json`](differences.md#exception-context) - source positions after edits
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,95 +0,0 @@
|
||||
# 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.
|
||||
|
||||
One thing differs when a `from_json()` reads the view: members looked up by name (`at()`, `value()`, ... and thus
|
||||
the macros) find the **first** member with a repeated key, see [Duplicate keys](differences.md#duplicate-keys);
|
||||
`#!cpp materialize().get<T>()` would see the last one. Containers built from an object, such as `#!cpp std::map`,
|
||||
keep the **last** value either way, as [`parse()`](../../api/basic_json/parse.md) does.
|
||||
|
||||
`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.
|
||||
@@ -1,70 +0,0 @@
|
||||
# Images
|
||||
|
||||
[`save()`](../../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
|
||||
[`load()`](../../api/basic_json_document/load.md) reads back into a document without parsing — no lexing, no building
|
||||
the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where
|
||||
[`parse_copy()`](../../api/basic_json_document/parse_copy.md) still has to scan the whole input,
|
||||
[`load()`](../../api/basic_json_document/load.md) turns that scan into a copy of the node index alone.
|
||||
|
||||
## Why
|
||||
|
||||
A document that is parsed once and then read many times — a configuration loaded at startup, a template rendered on
|
||||
every request, a large reference dataset a worker process needs in memory — pays for parsing once but can amortize
|
||||
[`save()`](../../api/basic_json_document/save.md)'s cost across every later load. That makes images useful for a
|
||||
cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache), and
|
||||
[`load()`](../../api/basic_json_document/load.md) it on every later use instead of parsing the source text again.
|
||||
They are just as useful for handing a parsed document to another process (or a forked worker) running the same build
|
||||
of the library, since [`load()`](../../api/basic_json_document/load.md) turns the transfer into a copy of the node
|
||||
index plus pointers into the received bytes, not a re-parse.
|
||||
|
||||
## Choosing a check
|
||||
|
||||
[`load()`](../../api/basic_json_document/load.md) takes an
|
||||
[`image_check`](../../api/basic_json_document/load.md#image_check) that trades validation against speed:
|
||||
|
||||
- `image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as
|
||||
safe to read and serialize as a freshly parsed document — the right choice whenever the image did not come straight
|
||||
from this process's own [`save()`](../../api/basic_json_document/save.md), such as a file or a network peer.
|
||||
- `image_check::bounds` only checks structure and bounds — cheaper, since it skips scanning the text and the decoded
|
||||
strings — and fits a cache the process trusts, one it wrote and reads back itself.
|
||||
- `image_check::none` skips validation entirely, for an image trusted as much as the process's own memory.
|
||||
|
||||
See [`load()`'s Notes](../../api/basic_json_document/load.md#notes) for exactly what each level does and does not
|
||||
guarantee.
|
||||
|
||||
??? example "Example: cache a parsed configuration as an image"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__save.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__save.output"
|
||||
```
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||
stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the
|
||||
library build that wrote it — use it to cache a document or to hand one to another process running the *same*
|
||||
build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by
|
||||
a future library version.
|
||||
|
||||
## Credits
|
||||
|
||||
The idea of a document you can read without parsing comes from zero-copy formats such as
|
||||
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
||||
[`load()`](../../api/basic_json_document/load.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from
|
||||
either.
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`save()`](../../api/basic_json_document/save.md) - write a document as an image
|
||||
- [`load()`](../../api/basic_json_document/load.md) - read an image back into a document
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,106 +0,0 @@
|
||||
# Zero-copy JSON views
|
||||
|
||||
`#!cpp <nlohmann/json_view.hpp>` adds a read-only, non-owning way to look at a parsed JSON text, as an alternative to
|
||||
building a [`basic_json`](../../api/basic_json/index.md) tree with [`parse()`](../../api/basic_json/parse.md).
|
||||
|
||||
## The problem
|
||||
|
||||
[`basic_json::parse()`](../../api/basic_json/parse.md) builds a tree of `basic_json` values: one allocation for every
|
||||
array and object, and every string copied into its own `std::string`. That is the right trade-off when the program
|
||||
goes on to read and write the value freely, but it does more work than necessary when only a small part of a large
|
||||
JSON text is actually needed, or when the same text is parsed over and over (many small messages, for instance) and
|
||||
most of the resulting tree is thrown away almost immediately.
|
||||
|
||||
## The idea
|
||||
|
||||
[`basic_json_document::parse()`](../../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
||||
options, but instead of a tree it builds a flat index of the values it found: one
|
||||
[16-byte entry](../../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
||||
order. Integers are converted while parsing and stored in their entry. Floating-point numbers and strings are
|
||||
referenced in place in the source text: a float is converted when it is read, and only strings that contain escape
|
||||
sequences are decoded, into one shared buffer owned by the document.
|
||||
|
||||
[`basic_json_view`](../../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
||||
index. It gives you the read-only, type-inspection part of the `basic_json` interface —
|
||||
[`type()`](../../api/basic_json_view/type.md) and the `is_*()` predicates,
|
||||
[`size()`](../../api/basic_json_view/size.md)/[`empty()`](../../api/basic_json_view/empty.md) — as well as element
|
||||
access ([`operator[]`](../../api/basic_json_view/operator%5B%5D.md), [`at`](../../api/basic_json_view/at.md),
|
||||
[`front`](../../api/basic_json_view/front.md)/[`back`](../../api/basic_json_view/back.md)), lookup
|
||||
([`find`](../../api/basic_json_view/find.md), [`contains`](../../api/basic_json_view/contains.md),
|
||||
[`count`](../../api/basic_json_view/count.md)), and iteration
|
||||
([`begin`](../../api/basic_json_view/begin.md)/[`end`](../../api/basic_json_view/end.md),
|
||||
[`items`](../../api/basic_json_view/items.md)) — without ever allocating a `basic_json` value. When you do need an
|
||||
actual `basic_json` value for a subtree, [`materialize()`](../../api/basic_json_view/materialize.md) builds exactly the
|
||||
one [`parse()`](../../api/basic_json/parse.md) would have produced for it.
|
||||
|
||||
The same small text, parsed both ways:
|
||||
|
||||
<div class="nj-fig-wrap" markdown>
|
||||
--8<-- "images/json_view_layout.svg"
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>Top: the tree of separate heap allocations that `basic_json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.</p>
|
||||
|
||||
## How to use it
|
||||
|
||||
Include `<nlohmann/json_view.hpp>` in addition to (or instead of) `<nlohmann/json.hpp>`. Parse into a
|
||||
[`json_document`](../../api/json_document.md), inspect its [`root()`](../../api/basic_json_document/root.md), and
|
||||
`materialize()` when you need a real value. `materialize()` works on any view, not just the root, so you can turn just
|
||||
the subtree you need into a value:
|
||||
|
||||
??? example "Example: parse a document, inspect its root, and materialize it"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_document.output"
|
||||
```
|
||||
|
||||
[`ordered_json_document`](../../api/ordered_json_document.md) is the equivalent for
|
||||
[`ordered_json`](../../api/ordered_json.md), just as [`ordered_json`](../../api/ordered_json.md) is to
|
||||
[`json`](../../api/json.md).
|
||||
|
||||
A view behaves like a `basic_json` value in most respects, but not in all: lookups of a repeated key, the order in
|
||||
which an object's members are visited, and what a missing key does to chained `operator[]` are the most visible
|
||||
differences. [Differences from `basic_json`](differences.md) lists all of them.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Ownership and lifetime](lifetime.md) - when a document borrows its source text, and how long a view stays valid
|
||||
- [Differences from `basic_json`](differences.md) - everything a view does differently from a parsed `basic_json` value
|
||||
- [Reading values](reading.md) - converting with `get<T>()`, reading strings and numbers without copying, and writing a
|
||||
view back with `dump()`
|
||||
- [Writing generic code](generic_code.md) - functions and `from_json()` overloads that accept views
|
||||
- [Editing a document](editing.md) - patching a document in place, keeping its member order and number spellings
|
||||
- [Images](images.md) - saving a parsed document and loading it back without parsing
|
||||
|
||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||
|
||||
| | [`json`](../../api/json.md) / [`ordered_json`](../../api/ordered_json.md) | [SAX interface](../parsing/sax_interface.md) | [`json_document`](../../api/json_document.md) / [`json_view`](../../api/json_view.md) | [`json_editable_document`](../../api/json_editable_document.md) / [`json_editable_view`](../../api/json_editable_view.md) |
|
||||
|---|---|---|---|---|
|
||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
|
||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../../api/basic_json_document/set.md)/[`push_back`](../../api/basic_json_document/push_back.md)/[`insert`](../../api/basic_json_document/insert.md)/[`erase`](../../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
|
||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../../api/basic_json_view/number_format.md), the spelling of every untouched number |
|
||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was |
|
||||
| **Caching/reload** | not applicable — re-parse, or roll your own serialization | not applicable | [`save()`](../../api/basic_json_document/save.md)/[`load()`](../../api/basic_json_document/load.md): cache the parsed index as an [image](images.md) and reload it without parsing | same, saving the document's current — possibly edited — state |
|
||||
|
||||
<p class="nj-table-caption" markdown>Each column is one way to work with JSON in this library, and each row compares one property of them.</p>
|
||||
|
||||
## See also
|
||||
|
||||
- [`basic_json_document`](../../api/basic_json_document/index.md) - the document that owns the parsed index (parse, access, images, edits)
|
||||
- [`basic_json_view`](../../api/basic_json_view/index.md) - the non-owning handle to a value in a document
|
||||
- [`json_document`](../../api/json_document.md) - and [`ordered_json_document`](../../api/ordered_json_document.md) - the read-only documents
|
||||
- [`json_editable_document`](../../api/json_editable_document.md) - and [`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
||||
- [Architecture](../../home/architecture.md#node-index-of-json-views) - the layout of the node index
|
||||
- [SAX interface](../parsing/sax_interface.md) - event-based parsing without building a tree
|
||||
- [Performance](../performance.md) - benchmarks and tuning of parsing and serialization
|
||||
- [Large, Streamed, and Partial Input](../parsing/large_and_partial_input.md) - reading only the part of a text you need
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,73 +0,0 @@
|
||||
# Ownership and lifetime
|
||||
|
||||
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call
|
||||
[`owns_source()`](../../api/basic_json_document/owns_source.md) to find out which happened.
|
||||
[`parse()`](../../api/basic_json_document/parse.md) decides this from the value category and type of its argument (an
|
||||
lvalue `#!cpp std::string` is borrowed; an rvalue `#!cpp std::string` is moved in, owned without a copy; a stream is
|
||||
read into an owned buffer; and so on — see [`parse`'s Notes](../../api/basic_json_document/parse.md#notes) for the
|
||||
full table). [`parse_copy()`](../../api/basic_json_document/parse_copy.md) always owns a copy, regardless of the input.
|
||||
|
||||
!!! warning "A borrowed document depends on your buffer"
|
||||
|
||||
If a document borrows its text, that text **must outlive the document** (and every view taken from it) and must
|
||||
stay **unmodified** for as long as the document exists, since floating-point values and unescaped strings are
|
||||
read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior,
|
||||
exactly as it would be for a dangling `#!cpp std::string_view`.
|
||||
|
||||
## When a view is valid
|
||||
|
||||
A view is valid only while all of the following hold:
|
||||
|
||||
- the document is alive,
|
||||
- the document has not been re-parsed since the view was taken (with
|
||||
[`read()`](../../api/basic_json_document/read.md) or [`parse()`](../../api/basic_json_document/parse.md) into it),
|
||||
- the document has not had [`shrink_to_fit()`](../../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
||||
- if the document borrows its source text, that text is still alive and unmodified.
|
||||
|
||||
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
||||
allocation that keeps its address across the move. Take a fresh view from
|
||||
[`root()`](../../api/basic_json_document/root.md) whenever any of the other conditions above was not met. Edits of an
|
||||
[editable document](editing.md) keep views valid, too; see [What stays valid](editing.md#what-stays-valid-and-what-an-edit-costs).
|
||||
|
||||
<div class="nj-fig-wrap" markdown>
|
||||
--8<-- "images/json_view_lifetime.svg"
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.</p>
|
||||
|
||||
## Why views can dangle
|
||||
|
||||
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
|
||||
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
|
||||
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
|
||||
and a `lock()` to every access — exactly the costs the view exists to avoid. What is caught is the obvious case:
|
||||
because a view dies with its document, [`root()`](../../api/basic_json_document/root.md) is not callable on a
|
||||
temporary document: `#!cpp auto v = json_document::parse(text).root();` does not compile. Give the document a name
|
||||
first.
|
||||
|
||||
The same holds for what a view hands out without copying: a string from
|
||||
[`get_string()`](../../api/basic_json_view/get_string.md), a token from
|
||||
[`number_token()`](../../api/basic_json_view/number_token.md), and [`string_view` members](reading.md#string_view-members)
|
||||
of decoded structs are valid only as long as the document and, if it borrows, its source text.
|
||||
|
||||
??? example "Example: borrowed and owned documents, and when views become invalid"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_view_ownership.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_view_ownership.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`basic_json_document`](../../api/basic_json_document/index.md) - the document that owns the parsed index
|
||||
- [`owns_source()`](../../api/basic_json_document/owns_source.md) - whether a document borrows its source text
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,87 +0,0 @@
|
||||
# Reading values
|
||||
|
||||
## Getting values out without copying
|
||||
|
||||
[`get<T>()`](../../api/basic_json_view/get.md) converts most `T` directly from the flat index, without ever building a
|
||||
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
|
||||
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json_view` itself, the standard containers
|
||||
and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::list`, `#!cpp std::array`,
|
||||
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, ...), and the types of the `NLOHMANN_DEFINE_TYPE_*` and
|
||||
`NLOHMANN_JSON_SERIALIZE_ENUM` macros. Types whose `from_json()` is written for `basic_json` only go through
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
|
||||
`basic_json` value first, exactly as [`parse()`](../../api/basic_json/parse.md) would, and converted from there. See
|
||||
[A `from_json()` that reads views](generic_code.md#a-from_json-that-reads-views) for how to write a `from_json()` that
|
||||
reads views, and [Duplicate keys](differences.md#duplicate-keys) for the one case in which the two ways give different
|
||||
results.
|
||||
|
||||
Two conversions never copy at all:
|
||||
|
||||
- [`get_string()`](../../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
||||
string as a `string_view_t` pointing into the document's [`source()`](../../api/basic_json_document/source.md) text
|
||||
— or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of
|
||||
allocating a new `#!cpp std::string`.
|
||||
- [`number_token()`](../../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
||||
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
||||
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
||||
[`basic_json::parse()`](../../api/basic_json/parse.md)) would.
|
||||
|
||||
Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.
|
||||
|
||||
### `string_view` members
|
||||
|
||||
A struct whose member is a `#!cpp std::string_view` (C++17) can be decoded from a view with the
|
||||
`NLOHMANN_DEFINE_TYPE_*` macros, and the member is not copied either: for a string without escape sequences it points
|
||||
into the document's source text, and for a string with escape sequences into the document's buffer of decoded
|
||||
strings. The member is valid as long as the document is alive and, for a document that
|
||||
[borrows its source text](lifetime.md) (as [`parse()`](../../api/basic_json_document/parse.md) does for an lvalue
|
||||
`#!cpp std::string`, a `#!cpp std::string_view`, or a C string), as long as that text is.
|
||||
|
||||
```cpp
|
||||
struct record
|
||||
{
|
||||
std::string_view name;
|
||||
int count;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||
};
|
||||
|
||||
const std::string text = R"({"name": "widget", "count": 3})";
|
||||
const json_document doc = json_document::parse(text); // borrows text
|
||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||
```
|
||||
|
||||
!!! warning "Decoding into a `string_view` from a `json` value dangles at once"
|
||||
|
||||
The same struct also compiles with `#!cpp json::parse(text).get<record>()`, but there `name` points into the
|
||||
temporary `json` value, which is destroyed at the end of the statement. Use a named
|
||||
[`json_document`](../../api/json_document.md), as above, to decode such a struct.
|
||||
|
||||
## Writing a view back
|
||||
|
||||
[`dump()`](../../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
|
||||
`basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a
|
||||
repeated key is written, not only the last one — the same two ways
|
||||
[iteration differs](differences.md#object-member-order) from a
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md)d value, see
|
||||
[Duplicate keys](differences.md#duplicate-keys). `#!cpp materialize().dump()` gives a different result in both
|
||||
respects for a `json_view`.
|
||||
|
||||
By default, numbers are written the way [`basic_json::dump()`](../../api/basic_json/dump.md) would.
|
||||
[`number_format::source`](../../api/basic_json_view/number_format.md) instead copies every number exactly as it was
|
||||
written in the source text — a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
|
||||
type holds, or a high-precision coordinate — something `basic_json` cannot do at all, since parsing already reduces
|
||||
a number to its parsed `#!cpp double`/`#!cpp int64_t` value. Together with an [editable document](editing.md), this
|
||||
lets you patch a file without reformatting the rest of it.
|
||||
|
||||
[`operator<<`](../../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
|
||||
the stream's `width`/`fill` for indentation.
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`get`](../../api/basic_json_view/get.md) - convert a view
|
||||
- [`dump`](../../api/basic_json_view/dump.md) - serialize a view
|
||||
- [Writing generic code](generic_code.md) - functions and `from_json()` overloads that accept views
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -88,7 +88,7 @@ a.erase(1); // [1,3,4] (erase by index)
|
||||
## Editing a document
|
||||
|
||||
The operations above work on a `basic_json` value. A JSON text that was parsed into a
|
||||
[document](json_view/editing.md) can also be changed in place, without building a `basic_json` tree for the
|
||||
[document](json_view.md#editing-a-document) can also be changed in place, without building a `basic_json` tree for the
|
||||
parts that are not touched: [`set`](../api/basic_json_document/set.md) replaces a value, adds or replaces an object
|
||||
member, or replaces an array element (also through a JSON pointer),
|
||||
[`push_back`](../api/basic_json_document/push_back.md) appends to an array,
|
||||
@@ -105,7 +105,7 @@ editable document types, such as [`json_editable_document`](../api/json_editable
|
||||
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch
|
||||
- [`erase`](../api/basic_json/erase.md) - / [`clear`](../api/basic_json/clear.md) - remove elements
|
||||
- [JSON Patch and Diff](json_patch.md) - and [JSON Merge Patch](merge_patch.md) - structured modifications
|
||||
- [Editing a document](json_view/editing.md) - change a parsed document in place
|
||||
- [Editing a document](json_view.md#editing-a-document) - change a parsed document in place
|
||||
- [`insert`](../api/basic_json/insert.md) - insert elements at an iterator position or merge a range
|
||||
- [`operator+=`](../api/basic_json/operator+=.md) - shorthand for `push_back`
|
||||
- [Element Access](element_access/index.md) - read elements
|
||||
|
||||
@@ -110,7 +110,7 @@ The library offers two parsing models, and a third, read-only alternative:
|
||||
reads the same JSON grammar but builds a flat, read-only index of the values instead of a `basic_json` tree. Strings and
|
||||
floating-point numbers are read from the text on demand, and only the parts you
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) become `basic_json` values. This avoids most of the work when you
|
||||
need only a few values from a large or frequently parsed text; see [Zero-copy JSON views](../json_view/index.md). It is read-only, so use
|
||||
need only a few values from a large or frequently parsed text; see [Zero-copy JSON views](../json_view.md). It is read-only, so use
|
||||
`parse` when you need to modify the value.
|
||||
|
||||
You can influence a DOM parse without switching to the SAX interface by passing a
|
||||
@@ -134,7 +134,7 @@ json_document doc = json_document::parse(R"({"happy": true, "pi": 3.141})");
|
||||
std::cout << doc.root()["happy"].get<bool>() << '\n';
|
||||
```
|
||||
|
||||
See [Zero-copy JSON views](../json_view/index.md) for the concept and the trade-offs.
|
||||
See [Zero-copy JSON views](../json_view.md) for the concept and the trade-offs.
|
||||
|
||||
## Exceptions
|
||||
|
||||
@@ -153,7 +153,7 @@ options.
|
||||
- [Large, streamed, and partial input](large_and_partial_input.md) - chunked, concatenated, and very large input
|
||||
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
|
||||
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
||||
- [Zero-copy JSON views](../json_view/index.md) - parse into a read-only document and read it through views
|
||||
- [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views
|
||||
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
||||
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
|
||||
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
|
||||
|
||||
@@ -156,11 +156,11 @@ record the reason in the handler (here: `id`) if you need to tell the two apart.
|
||||
|
||||
### Read parts of a large document with a view
|
||||
|
||||
If the whole text is available and you only need to read some of it, a [`json_document`](../json_view/index.md) from
|
||||
If the whole text is available and you only need to read some of it, a [`json_document`](../json_view.md) from
|
||||
`<nlohmann/json_view.hpp>` (added in version 3.13.0) avoids the `basic_json` tree altogether: it keeps the text, builds a flat index of the values,
|
||||
and gives read-only [views](../../api/basic_json_view/index.md) of the parts you look at. Only what you
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) becomes a `basic_json` value. See
|
||||
[Zero-copy JSON views](../json_view/index.md) for details and for the lifetime rules.
|
||||
[Zero-copy JSON views](../json_view.md) for details and for the lifetime rules.
|
||||
|
||||
## See also
|
||||
|
||||
@@ -168,7 +168,7 @@ and gives read-only [views](../../api/basic_json_view/index.md) of the parts you
|
||||
- [JSON Lines](json_lines.md) - newline-delimited JSON
|
||||
- [Parser callbacks](parser_callbacks.md) - discard values while parsing
|
||||
- [SAX interface](sax_interface.md) - event-based parsing
|
||||
- [Zero-copy JSON views](../json_view/index.md) - read parts of a large document without building a tree
|
||||
- [Zero-copy JSON views](../json_view.md) - read parts of a large document without building a tree
|
||||
- [Untrusted input](untrusted_input.md) - limits for input you do not control
|
||||
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
|
||||
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
|
||||
|
||||
@@ -71,12 +71,12 @@ a DOM, three alternatives avoid building it:
|
||||
discard finished elements as soon as they are handled, so memory usage stays bounded by one element (plus the
|
||||
unparsed remainder of the input) instead of the whole document — see the [recipe for streaming a large homogeneous
|
||||
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
|
||||
- Parse into a [`json_document`](json_view/index.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
||||
- Parse into a [`json_document`](json_view.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
||||
the input text and builds a flat index of 16 bytes per value; strings and floating-point numbers are not copied, but
|
||||
read from the text when needed. Read-only [views](../api/basic_json_view/index.md) give the familiar element access, and only the
|
||||
parts you [`materialize()`](../api/basic_json_view/materialize.md) become `basic_json` values. A document that
|
||||
borrows the text instead of owning a copy needs the text to outlive it; see
|
||||
[choosing between `json`, the SAX interface, and `json_view`](json_view/index.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
||||
[choosing between `json`, the SAX interface, and `json_view`](json_view.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
||||
|
||||
If the data is naturally record-oriented, consider [JSON Lines](parsing/json_lines.md) instead of one large JSON
|
||||
document: reading and parsing it line by line with `#!cpp std::getline` means only one line's value is ever in memory
|
||||
@@ -180,11 +180,11 @@ an adapter, since their template argument order does not match what `basic_json`
|
||||
large value therefore doubles its memory; see [Avoiding copies](#avoiding-copies). `#!cpp auto x = j["key"];` is a
|
||||
copy, `#!cpp auto& x = j["key"];` is not.
|
||||
- **A moved-from value is `null`.** After `#!cpp json c = std::move(a);` the value `a` is `#!json null` and owns nothing.
|
||||
- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view/index.md)
|
||||
- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view.md)
|
||||
(`#!cpp <nlohmann/json_view.hpp>`, added in version 3.13.0) instead of a `basic_json`. It stores 16 bytes per value, does not copy floating-point
|
||||
numbers or strings without escape sequences out of the text, and creates no `basic_json` value except for the parts
|
||||
you [`materialize()`](../api/basic_json_view/materialize.md). A document that borrows the text needs the text to
|
||||
outlive it; see [Zero-copy JSON views](json_view/index.md).
|
||||
outlive it; see [Zero-copy JSON views](json_view.md).
|
||||
- **Containers keep their capacity.** `clear()` and `erase()` on an array remove the elements, but the underlying
|
||||
`#!cpp std::vector` keeps its allocated capacity, like `#!cpp std::vector::clear` does. To give the memory back,
|
||||
assign a new value (`#!cpp j = json::array();`) or destroy the value.
|
||||
@@ -265,7 +265,7 @@ those headers are then never processed by the compiler at all.
|
||||
- [Architecture](../home/architecture.md) - how input adapters, the lexer, and the serializer fit together
|
||||
- [Parsing](parsing/index.md) - the available parsing functions and inputs
|
||||
- [SAX interface](parsing/sax_interface.md) - parse without building a DOM
|
||||
- [Zero-copy JSON views](json_view/index.md) - parse into a flat index of the text and read it without building a DOM
|
||||
- [Zero-copy JSON views](json_view.md) - parse into a flat index of the text and read it without building a DOM
|
||||
- [Binary formats](binary_formats/index.md) - compact alternatives to JSON text
|
||||
- [Object Order](object_order.md) - `json` vs. `ordered_json` and other `ObjectType` choices
|
||||
- [Template Parameter Requirements](types/template_parameters.md) - custom container and allocator types
|
||||
|
||||
@@ -56,7 +56,7 @@ The public headers are in [`include/nlohmann`](https://github.com/nlohmann/json/
|
||||
- [`json_view.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_view.hpp) is a separate,
|
||||
optional header that defines [`basic_json_document`](../api/basic_json_document/index.md) and
|
||||
[`basic_json_view`](../api/basic_json_view/index.md), a flat-index, read-only, non-owning way to look at a parsed
|
||||
JSON text; see [Zero-copy JSON views](../features/json_view/index.md). It builds on `json.hpp` internals (it requires the
|
||||
JSON text; see [Zero-copy JSON views](../features/json_view.md). It builds on `json.hpp` internals (it requires the
|
||||
same library version) and has its own `detail/view/` subdirectory.
|
||||
|
||||
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
|
||||
@@ -175,7 +175,7 @@ maintains the invariant that the pointer matching `m_type` is never null; `asser
|
||||
|
||||
## Node index of JSON views
|
||||
|
||||
A [`basic_json_document`](../api/basic_json_document/index.md) (see [Zero-copy JSON views](../features/json_view/index.md))
|
||||
A [`basic_json_document`](../api/basic_json_document/index.md) (see [Zero-copy JSON views](../features/json_view.md))
|
||||
does not build a tree of values. Its parser
|
||||
([`detail/view/builder.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/builder.hpp))
|
||||
writes a flat array of 16-byte nodes, one per value and one per object key, in document order. A
|
||||
@@ -466,7 +466,7 @@ release. Besides the components above, it contains:
|
||||
- [`adl_serializer`](../api/adl_serializer/index.md) - customization point for type conversions
|
||||
- [`json_sax`](../api/json_sax/index.md) - interface of the SAX parser events
|
||||
- [SAX Interface](../features/parsing/sax_interface.md) - how to consume parser events without building a DOM
|
||||
- [Zero-copy JSON views](../features/json_view/index.md) - the flat node index that backs views and documents
|
||||
- [Zero-copy JSON views](../features/json_view.md) - the flat node index that backs views and documents
|
||||
- [Template Parameter Requirements](../features/types/template_parameters.md) - the template parameters of basic_json
|
||||
- [Serialization](../features/serialization.md) - the output side of the library
|
||||
- [`nlohmann` Namespace](../features/namespace.md) - how the library namespace is structured
|
||||
@@ -438,7 +438,7 @@ A UBJSON or BJData high-precision number could not be parsed.
|
||||
### json.exception.parse_error.116
|
||||
|
||||
[`basic_json_document::load()`](../api/basic_json_document/load.md) rejected an
|
||||
[image](../features/json_view/images.md): either the bytes are not one [`save()`](../api/basic_json_document/save.md)
|
||||
[image](../features/json_view.md#images): either the bytes are not one [`save()`](../api/basic_json_document/save.md)
|
||||
could have written (too short, an unknown magic number or format version, or sizes that do not fit the buffer), or
|
||||
they are, but fail the requested [`image_check`](../api/basic_json_document/load.md#image_check).
|
||||
|
||||
@@ -450,7 +450,7 @@ they are, but fail the requested [`image_check`](../api/basic_json_document/load
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view/images.md).
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
||||
|
||||
## Iterator errors
|
||||
|
||||
@@ -889,12 +889,12 @@ from JSON text.
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view/index.md).
|
||||
This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view.md).
|
||||
|
||||
### json.exception.type_error.320
|
||||
|
||||
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
|
||||
[image](../features/json_view/images.md) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
||||
[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
||||
|
||||
!!! failure "Example messages"
|
||||
|
||||
@@ -904,7 +904,7 @@ from JSON text.
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view/images.md).
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
||||
|
||||
### json.exception.type_error.321
|
||||
|
||||
@@ -1192,7 +1192,7 @@ so they do not support an input of 4294967280 bytes (4 GiB minus 16 bytes) or mo
|
||||
storage: [`set`](../api/basic_json_document/set.md) and [`push_back`](../api/basic_json_document/push_back.md) throw
|
||||
this exception once the strings and number tokens written by edits reach 4 GiB in total, or once more than
|
||||
4294967295 arrays/objects have had an element set or appended to them. The same limit applies to an
|
||||
[image](../features/json_view/images.md): [`save()`](../api/basic_json_document/save.md) throws it if the node
|
||||
[image](../features/json_view.md#images): [`save()`](../api/basic_json_document/save.md) throws it if the node
|
||||
count, the text, or the decoded strings it would write would individually reach 4 GiB.
|
||||
|
||||
!!! failure "Example messages"
|
||||
@@ -1209,7 +1209,7 @@ count, the text, or the decoded strings it would write would individually reach
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with [`<nlohmann/json_view.hpp>`](../features/json_view/index.md).
|
||||
This exception was added in version 3.13.0, together with [`<nlohmann/json_view.hpp>`](../features/json_view.md).
|
||||
[`basic_json::parse()`](../api/basic_json/parse.md) has no such limit.
|
||||
|
||||
## Further exceptions
|
||||
|
||||
@@ -140,7 +140,7 @@ Read newline-delimited input line by line ([JSON Lines](../features/parsing/json
|
||||
|
||||
Handle and discard the records of a large array with a [parser callback](../features/parsing/parser_callbacks.md), stop
|
||||
early with the [SAX interface](../features/parsing/sax_interface.md), or read parts of the text through a
|
||||
[`json_document`](../features/json_view/index.md) (added in version 3.13.0). See
|
||||
[`json_document`](../features/json_view.md) (added in version 3.13.0). See
|
||||
[Input too large for memory](../features/parsing/large_and_partial_input.md#input-too-large-for-memory).
|
||||
|
||||
### Position of a parse error
|
||||
|
||||
@@ -58,7 +58,7 @@ Clang).
|
||||
anything, or link a library. Put the file in a directory named `nlohmann` that is on your include path (for
|
||||
example `third_party/nlohmann/json.hpp` with `-I third_party`), so that `#include <nlohmann/json.hpp>` works.
|
||||
Download the companion headers listed below only if you use them: `json_view.hpp` for the
|
||||
[zero-copy views](../features/json_view/index.md), `json_fwd.hpp` for forward declarations, and `json_literals.hpp` if
|
||||
[zero-copy views](../features/json_view.md), `json_fwd.hpp` for forward declarations, and `json_literals.hpp` if
|
||||
you define `JSON_NO_AUTOMATIC_UDLS`. Keep them in the same `nlohmann` directory.
|
||||
|
||||
Do not copy only parts of the repository's `include/` directory: the headers in it include each other and must be
|
||||
@@ -75,7 +75,7 @@ for the user-defined string literals if you define
|
||||
For the read-only, non-owning [`basic_json_document`](../api/basic_json_document/index.md)/[`basic_json_view`](../api/basic_json_view/index.md)
|
||||
types, additionally include
|
||||
[`single_include/nlohmann/json_view.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_view.hpp);
|
||||
see [Zero-copy JSON views](../features/json_view/index.md).
|
||||
see [Zero-copy JSON views](../features/json_view.md).
|
||||
|
||||
## Notes for builds and distribution
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* figures: jsonview -- stepper for the edit frames on features/json_view/index.md.
|
||||
/* figures: jsonview -- stepper for the edit frames on features/json_view.md.
|
||||
Without JS the frames (.nj-jsonview-step) show one below the other; with JS one frame at a time, with
|
||||
previous/next and one button per step. Never advances on its own. */
|
||||
(function () {
|
||||
|
||||
@@ -157,14 +157,7 @@ nav:
|
||||
- features/types/index.md
|
||||
- features/types/number_handling.md
|
||||
- features/types/template_parameters.md
|
||||
- Zero-copy views:
|
||||
- features/json_view/index.md
|
||||
- features/json_view/lifetime.md
|
||||
- features/json_view/differences.md
|
||||
- features/json_view/reading.md
|
||||
- features/json_view/generic_code.md
|
||||
- features/json_view/editing.md
|
||||
- features/json_view/images.md
|
||||
- features/json_view.md
|
||||
- Integration:
|
||||
- integration/index.md
|
||||
- integration/migration_guide.md
|
||||
|
||||
@@ -1168,20 +1168,19 @@ indent_done:
|
||||
if (negative)
|
||||
{
|
||||
++p;
|
||||
if (NLOHMANN_VIEW_UNLIKELY(cur() < '0' || cur() > '9'))
|
||||
{
|
||||
return fail(error_code::number_after_minus);
|
||||
}
|
||||
}
|
||||
const unsigned char* const int_start = p;
|
||||
if (cur() == '0')
|
||||
{
|
||||
++p;
|
||||
}
|
||||
else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
|
||||
{
|
||||
p = skip_digits(p + 1, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (a digit: the dispatch or the check after the sign saw it)
|
||||
p = skip_digits(p + 1, e);
|
||||
return fail(error_code::number_after_minus);
|
||||
}
|
||||
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
||||
std::size_t frac_digits = 0;
|
||||
|
||||
Generated
+5
-6
@@ -2473,20 +2473,19 @@ indent_done:
|
||||
if (negative)
|
||||
{
|
||||
++p;
|
||||
if (NLOHMANN_VIEW_UNLIKELY(cur() < '0' || cur() > '9'))
|
||||
{
|
||||
return fail(error_code::number_after_minus);
|
||||
}
|
||||
}
|
||||
const unsigned char* const int_start = p;
|
||||
if (cur() == '0')
|
||||
{
|
||||
++p;
|
||||
}
|
||||
else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
|
||||
{
|
||||
p = skip_digits(p + 1, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (a digit: the dispatch or the check after the sign saw it)
|
||||
p = skip_digits(p + 1, e);
|
||||
return fail(error_code::number_after_minus);
|
||||
}
|
||||
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
||||
std::size_t frac_digits = 0;
|
||||
|
||||
@@ -1026,7 +1026,10 @@ TEST_CASE("insert() provides its documented exception guarantees (#5986)")
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
// the no-exceptions CI job has no std::bad_alloc to catch; with iterator
|
||||
// debugging (MSVC STL debug builds) containers allocate inside noexcept
|
||||
// functions, so failing an allocation there terminates the program
|
||||
#if !defined(JSON_NOEXCEPTION) && !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||
namespace
|
||||
{
|
||||
// the allocation numbered sweep_fail_at (counted from 1) throws; 0 never fails
|
||||
|
||||
@@ -505,8 +505,8 @@ struct locale_switching_token
|
||||
{
|
||||
using value_type = char;
|
||||
|
||||
locale_switching_token(std::string text_, std::string switch_to_)
|
||||
: text(std::move(text_)), switch_to(std::move(switch_to_))
|
||||
locale_switching_token(std::string text_, const char* switch_to_)
|
||||
: text(std::move(text_)), switch_to(switch_to_)
|
||||
{}
|
||||
|
||||
std::size_t size() const
|
||||
@@ -523,13 +523,13 @@ struct locale_switching_token
|
||||
{
|
||||
if (!switched)
|
||||
{
|
||||
switched = std::setlocale(LC_NUMERIC, switch_to.c_str()) != nullptr;
|
||||
switched = std::setlocale(LC_NUMERIC, switch_to) != nullptr;
|
||||
}
|
||||
return &text[0];
|
||||
}
|
||||
|
||||
std::string text;
|
||||
std::string switch_to;
|
||||
const char* switch_to;
|
||||
bool switched = false;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user