diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md index 9349ca6fb..3300ba6d2 100644 --- a/docs/mkdocs/docs/api/basic_json/index.md +++ b/docs/mkdocs/docs/api/basic_json/index.md @@ -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 ``; see -[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs. +[Zero-copy JSON views](../../features/json_view/index.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.md) - when to use a document and a view instead of a `basic_json` +- [Zero-copy JSON views](../../features/json_view/index.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 diff --git a/docs/mkdocs/docs/api/basic_json/parse.md b/docs/mkdocs/docs/api/basic_json/parse.md index cf7a29d95..0ebcdb496 100644 --- a/docs/mkdocs/docs/api/basic_json/parse.md +++ b/docs/mkdocs/docs/api/basic_json/parse.md @@ -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.md). + which `parse()` does not. See [Zero-copy JSON views](../../features/json_view/index.md). !!! tip "Performance" diff --git a/docs/mkdocs/docs/api/basic_json_document/accept.md b/docs/mkdocs/docs/api/basic_json_document/accept.md index 766fd20fa..22a61b15e 100644 --- a/docs/mkdocs/docs/api/basic_json_document/accept.md +++ b/docs/mkdocs/docs/api/basic_json_document/accept.md @@ -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.md#what-is-the-same-as-parse) - how parsing a document differs from, and matches, basic_json::parse +- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how parsing a document differs from, and matches, basic_json::parse ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/basic_json_document.md b/docs/mkdocs/docs/api/basic_json_document/basic_json_document.md index c0cd5d0ad..b470de835 100644 --- a/docs/mkdocs/docs/api/basic_json_document/basic_json_document.md +++ b/docs/mkdocs/docs/api/basic_json_document/basic_json_document.md @@ -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.md#ownership-and-lifetime) - who owns the source text and for how long +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/erase.md b/docs/mkdocs/docs/api/basic_json_document/erase.md index db6dc523b..02a202a1d 100644 --- a/docs/mkdocs/docs/api/basic_json_document/erase.md +++ b/docs/mkdocs/docs/api/basic_json_document/erase.md @@ -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.md#editing-a-document) - what an edit guarantees and costs +- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/index.md b/docs/mkdocs/docs/api/basic_json_document/index.md index b38105623..9b1cb35d7 100644 --- a/docs/mkdocs/docs/api/basic_json_document/index.md +++ b/docs/mkdocs/docs/api/basic_json_document/index.md @@ -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.md#editing-a-document) for + number that was not itself edited — see [Editing a document](../../features/json_view/editing.md) 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.md) - guide to documents and views +- [Zero-copy JSON views](../../features/json_view/index.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 diff --git a/docs/mkdocs/docs/api/basic_json_document/insert.md b/docs/mkdocs/docs/api/basic_json_document/insert.md index 207e7bae8..37626fbe9 100644 --- a/docs/mkdocs/docs/api/basic_json_document/insert.md +++ b/docs/mkdocs/docs/api/basic_json_document/insert.md @@ -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.md#editing-a-document) - what an edit guarantees and costs +- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/is_discarded.md b/docs/mkdocs/docs/api/basic_json_document/is_discarded.md index 4e86a9748..f1d6e6f2d 100644 --- a/docs/mkdocs/docs/api/basic_json_document/is_discarded.md +++ b/docs/mkdocs/docs/api/basic_json_document/is_discarded.md @@ -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.md#what-is-the-same-as-parse) - how a discarded document arises +- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how a discarded document arises ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/load.md b/docs/mkdocs/docs/api/basic_json_document/load.md index 21edbaf87..46a71cb06 100644 --- a/docs/mkdocs/docs/api/basic_json_document/load.md +++ b/docs/mkdocs/docs/api/basic_json_document/load.md @@ -24,7 +24,7 @@ static basic_json_document load(std::vector&& 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.md) use for lookup are rebuilt, exactly as after +`image`. The hash indexes [large objects](../../features/json_view/index.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.md#images) - why and when to use images +- [Images](../../features/json_view/images.md) - why and when to use images ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/memory_usage.md b/docs/mkdocs/docs/api/basic_json_document/memory_usage.md index d265d623c..c96f0314a 100644 --- a/docs/mkdocs/docs/api/basic_json_document/memory_usage.md +++ b/docs/mkdocs/docs/api/basic_json_document/memory_usage.md @@ -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.md#the-idea) - the flat node index and its memory +- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/node_count.md b/docs/mkdocs/docs/api/basic_json_document/node_count.md index 5bb63a40d..5414a05c2 100644 --- a/docs/mkdocs/docs/api/basic_json_document/node_count.md +++ b/docs/mkdocs/docs/api/basic_json_document/node_count.md @@ -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.md#the-idea) - the flat node index and its memory +- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/owns_source.md b/docs/mkdocs/docs/api/basic_json_document/owns_source.md index f4068cab5..444e7f78f 100644 --- a/docs/mkdocs/docs/api/basic_json_document/owns_source.md +++ b/docs/mkdocs/docs/api/basic_json_document/owns_source.md @@ -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.md#ownership-and-lifetime) - who owns the source text and for how long +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/parse.md b/docs/mkdocs/docs/api/basic_json_document/parse.md index e458e621d..87a558c39 100644 --- a/docs/mkdocs/docs/api/basic_json_document/parse.md +++ b/docs/mkdocs/docs/api/basic_json_document/parse.md @@ -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`). See [`owns_source`](owns_source.md) to check which happened after a call, and the -[feature page](../../features/json_view.md) for the reasoning. +[feature page](../../features/json_view/index.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. diff --git a/docs/mkdocs/docs/api/basic_json_document/parse_copy.md b/docs/mkdocs/docs/api/basic_json_document/parse_copy.md index f9110207b..01239b8f2 100644 --- a/docs/mkdocs/docs/api/basic_json_document/parse_copy.md +++ b/docs/mkdocs/docs/api/basic_json_document/parse_copy.md @@ -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.md#ownership-and-lifetime) - borrowing or copying the source text +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - borrowing or copying the source text ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/push_back.md b/docs/mkdocs/docs/api/basic_json_document/push_back.md index f433fc7db..3a9adffa2 100644 --- a/docs/mkdocs/docs/api/basic_json_document/push_back.md +++ b/docs/mkdocs/docs/api/basic_json_document/push_back.md @@ -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.md#editing-a-document) - what an edit guarantees and costs +- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/read.md b/docs/mkdocs/docs/api/basic_json_document/read.md index 63915b1c2..1552c1007 100644 --- a/docs/mkdocs/docs/api/basic_json_document/read.md +++ b/docs/mkdocs/docs/api/basic_json_document/read.md @@ -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.md#ownership-and-lifetime) - reusing a document and what it invalidates +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - reusing a document and what it invalidates ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/root.md b/docs/mkdocs/docs/api/basic_json_document/root.md index 14e4514c2..864083205 100644 --- a/docs/mkdocs/docs/api/basic_json_document/root.md +++ b/docs/mkdocs/docs/api/basic_json_document/root.md @@ -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.md#ownership-and-lifetime). +`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view/lifetime.md). ## Examples diff --git a/docs/mkdocs/docs/api/basic_json_document/save.md b/docs/mkdocs/docs/api/basic_json_document/save.md index 466a15ea5..75e7e6ff3 100644 --- a/docs/mkdocs/docs/api/basic_json_document/save.md +++ b/docs/mkdocs/docs/api/basic_json_document/save.md @@ -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.md#images) - why and when to use images +- [Images](../../features/json_view/images.md) - why and when to use images ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/set.md b/docs/mkdocs/docs/api/basic_json_document/set.md index e3f05b151..5c68da311 100644 --- a/docs/mkdocs/docs/api/basic_json_document/set.md +++ b/docs/mkdocs/docs/api/basic_json_document/set.md @@ -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.md#editing-a-document) - why editable documents keep the source +- [Editing a document](../../features/json_view/editing.md) - why editable documents keep the source text's order and number spelling ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/shrink_to_fit.md b/docs/mkdocs/docs/api/basic_json_document/shrink_to_fit.md index b4ad53fba..458af9434 100644 --- a/docs/mkdocs/docs/api/basic_json_document/shrink_to_fit.md +++ b/docs/mkdocs/docs/api/basic_json_document/shrink_to_fit.md @@ -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.md#ownership-and-lifetime) - what releasing memory invalidates +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - what releasing memory invalidates ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_document/source.md b/docs/mkdocs/docs/api/basic_json_document/source.md index 688bb6bf3..a0c865195 100644 --- a/docs/mkdocs/docs/api/basic_json_document/source.md +++ b/docs/mkdocs/docs/api/basic_json_document/source.md @@ -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.md#ownership-and-lifetime) - who owns the source text and for how long +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/at.md b/docs/mkdocs/docs/api/basic_json_view/at.md index 7b810c4ed..ff2c72907 100644 --- a/docs/mkdocs/docs/api/basic_json_view/at.md +++ b/docs/mkdocs/docs/api/basic_json_view/at.md @@ -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.md#duplicate-keys) for the reasons and for how to get + `operator[]` and [Duplicate keys](../../features/json_view/differences.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 diff --git a/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md b/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md index b4aa4db00..cd8508a75 100644 --- a/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md +++ b/docs/mkdocs/docs/api/basic_json_view/basic_json_view.md @@ -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.md#ownership-and-lifetime) - how long a view stays valid +- [Zero-copy JSON views](../../features/json_view/lifetime.md) - how long a view stays valid ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/contains.md b/docs/mkdocs/docs/api/basic_json_view/contains.md index dc1208125..0abb37be7 100644 --- a/docs/mkdocs/docs/api/basic_json_view/contains.md +++ b/docs/mkdocs/docs/api/basic_json_view/contains.md @@ -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.md#duplicate-keys) for the reasons and for how to get + `operator[]` and [Duplicate keys](../../features/json_view/differences.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) diff --git a/docs/mkdocs/docs/api/basic_json_view/dump.md b/docs/mkdocs/docs/api/basic_json_view/dump.md index a47ca6085..2fd64a2ce 100644 --- a/docs/mkdocs/docs/api/basic_json_view/dump.md +++ b/docs/mkdocs/docs/api/basic_json_view/dump.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.md#what-is-different)) — something `BasicJsonType` cannot do, since +[What is different](../../features/json_view/differences.md)) — 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.md#writing-a-view-back) - writing a view back without building a tree +- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/find.md b/docs/mkdocs/docs/api/basic_json_view/find.md index b55741ca5..a48851d9e 100644 --- a/docs/mkdocs/docs/api/basic_json_view/find.md +++ b/docs/mkdocs/docs/api/basic_json_view/find.md @@ -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.md#duplicate-keys) for the reasons and for how to get + `operator[]` and [Duplicate keys](../../features/json_view/differences.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 diff --git a/docs/mkdocs/docs/api/basic_json_view/get.md b/docs/mkdocs/docs/api/basic_json_view/get.md index 1739d372b..a1d8f737f 100644 --- a/docs/mkdocs/docs/api/basic_json_view/get.md +++ b/docs/mkdocs/docs/api/basic_json_view/get.md @@ -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 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.md#writing-generic-code). +`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view/generic_code.md). !!! warning "Duplicate keys" Such a `from_json()` looks members up in the view, where a repeated key finds the **first** member, while `#!cpp materialize().get()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md) keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get()` reads `#!cpp x == 1`. See - [Duplicate keys](../../features/json_view.md#duplicate-keys). + [Duplicate keys](../../features/json_view/differences.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()`: 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.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value +- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/get_string.md b/docs/mkdocs/docs/api/basic_json_view/get_string.md index b7d4d4203..542ce4688 100644 --- a/docs/mkdocs/docs/api/basic_json_view/get_string.md +++ b/docs/mkdocs/docs/api/basic_json_view/get_string.md @@ -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()` 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.md#getting-values-out-without-copying) - borrowing strings instead of copying them +- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - borrowing strings instead of copying them ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/get_to.md b/docs/mkdocs/docs/api/basic_json_view/get_to.md index ae8a8228a..37bd11b24 100644 --- a/docs/mkdocs/docs/api/basic_json_view/get_to.md +++ b/docs/mkdocs/docs/api/basic_json_view/get_to.md @@ -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.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value +- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/index.md b/docs/mkdocs/docs/api/basic_json_view/index.md index f363d6433..d646f77e5 100644 --- a/docs/mkdocs/docs/api/basic_json_view/index.md +++ b/docs/mkdocs/docs/api/basic_json_view/index.md @@ -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.md) - guide to documents and views +- [Zero-copy JSON views](../../features/json_view/index.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 diff --git a/docs/mkdocs/docs/api/basic_json_view/is_discarded.md b/docs/mkdocs/docs/api/basic_json_view/is_discarded.md index e6751c12c..25cc956b7 100644 --- a/docs/mkdocs/docs/api/basic_json_view/is_discarded.md +++ b/docs/mkdocs/docs/api/basic_json_view/is_discarded.md @@ -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.md#what-is-different) - how views differ from basic_json +- [Zero-copy JSON views](../../features/json_view/differences.md) - how views differ from basic_json ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/materialize.md b/docs/mkdocs/docs/api/basic_json_view/materialize.md index 982b1597f..6dc081f24 100644 --- a/docs/mkdocs/docs/api/basic_json_view/materialize.md +++ b/docs/mkdocs/docs/api/basic_json_view/materialize.md @@ -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.md#getting-values-out-without-copying) - when to build a basic_json value from a view +- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - when to build a basic_json value from a view ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/number_format.md b/docs/mkdocs/docs/api/basic_json_view/number_format.md index 01c06193e..5a3601a62 100644 --- a/docs/mkdocs/docs/api/basic_json_view/number_format.md +++ b/docs/mkdocs/docs/api/basic_json_view/number_format.md @@ -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.md#writing-a-view-back) - writing numbers as they appeared in the source +- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing numbers as they appeared in the source ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/number_token.md b/docs/mkdocs/docs/api/basic_json_view/number_token.md index 6451cb1fa..8ee8ce348 100644 --- a/docs/mkdocs/docs/api/basic_json_view/number_token.md +++ b/docs/mkdocs/docs/api/basic_json_view/number_token.md @@ -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()` and `materialize()` convert into -- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - reading a number exactly as written +- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - reading a number exactly as written ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/operator[].md b/docs/mkdocs/docs/api/basic_json_view/operator[].md index a04bfd3b8..c217b7e73 100644 --- a/docs/mkdocs/docs/api/basic_json_view/operator[].md +++ b/docs/mkdocs/docs/api/basic_json_view/operator[].md @@ -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.md#duplicate-keys) and + duplicates, in document order. See [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) and [`size()`](size.md#notes). !!! info "JSON pointer resolution" diff --git a/docs/mkdocs/docs/api/basic_json_view/operator_eq.md b/docs/mkdocs/docs/api/basic_json_view/operator_eq.md index 26ff303f6..b385aa0a9 100644 --- a/docs/mkdocs/docs/api/basic_json_view/operator_eq.md +++ b/docs/mkdocs/docs/api/basic_json_view/operator_eq.md @@ -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.md#duplicate-keys)). +member (see [Duplicate keys](../../features/json_view/differences.md#duplicate-keys)). ## Parameters diff --git a/docs/mkdocs/docs/api/basic_json_view/operator_ltlt.md b/docs/mkdocs/docs/api/basic_json_view/operator_ltlt.md index 3ee5961fb..9c94297dd 100644 --- a/docs/mkdocs/docs/api/basic_json_view/operator_ltlt.md +++ b/docs/mkdocs/docs/api/basic_json_view/operator_ltlt.md @@ -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.md#writing-a-view-back) - writing a view back without building a tree +- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/source_offset.md b/docs/mkdocs/docs/api/basic_json_view/source_offset.md index fb24caff3..dbb3601b0 100644 --- a/docs/mkdocs/docs/api/basic_json_view/source_offset.md +++ b/docs/mkdocs/docs/api/basic_json_view/source_offset.md @@ -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.md#images) - images and the source text +- [Zero-copy JSON views](../../features/json_view/images.md) - images and the source text ## Version history diff --git a/docs/mkdocs/docs/api/basic_json_view/value.md b/docs/mkdocs/docs/api/basic_json_view/value.md index ebce7b4bf..6e4b20d09 100644 --- a/docs/mkdocs/docs/api/basic_json_view/value.md +++ b/docs/mkdocs/docs/api/basic_json_view/value.md @@ -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.md#duplicate-keys) for the reasons and for how to get + `operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get the last value. !!! info "Differences to `at` and `operator[]`" diff --git a/docs/mkdocs/docs/api/is_json_view.md b/docs/mkdocs/docs/api/is_json_view.md index 6153adf30..eaf11155e 100644 --- a/docs/mkdocs/docs/api/is_json_view.md +++ b/docs/mkdocs/docs/api/is_json_view.md @@ -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.md#writing-generic-code) - functions and `from_json()` for views +- [Writing generic code](../features/json_view/generic_code.md) - functions and `from_json()` for views ## Version history diff --git a/docs/mkdocs/docs/api/json_document.md b/docs/mkdocs/docs/api/json_document.md index 2000128ac..b21b9a730 100644 --- a/docs/mkdocs/docs/api/json_document.md +++ b/docs/mkdocs/docs/api/json_document.md @@ -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.md) - how to parse, inspect and edit a document without building a tree +- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree ## Version history diff --git a/docs/mkdocs/docs/api/json_editable_document.md b/docs/mkdocs/docs/api/json_editable_document.md index c455c8343..f40406093 100644 --- a/docs/mkdocs/docs/api/json_editable_document.md +++ b/docs/mkdocs/docs/api/json_editable_document.md @@ -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.md#editing-a-document). +[Editing a document](../features/json_view/editing.md). ## Examples diff --git a/docs/mkdocs/docs/api/json_editable_view.md b/docs/mkdocs/docs/api/json_editable_view.md index eede89703..3cc31fbdb 100644 --- a/docs/mkdocs/docs/api/json_editable_view.md +++ b/docs/mkdocs/docs/api/json_editable_view.md @@ -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.md#editing-a-document) - editing a document in place and writing it back +- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back ## Version history diff --git a/docs/mkdocs/docs/api/json_view.md b/docs/mkdocs/docs/api/json_view.md index 408214fe4..bf9e32a0c 100644 --- a/docs/mkdocs/docs/api/json_view.md +++ b/docs/mkdocs/docs/api/json_view.md @@ -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.md) - how to parse, inspect and edit a document without building a tree +- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree ## Version history diff --git a/docs/mkdocs/docs/api/macros/json_no_simd.md b/docs/mkdocs/docs/api/macros/json_no_simd.md index b16202b12..26fcfe550 100644 --- a/docs/mkdocs/docs/api/macros/json_no_simd.md +++ b/docs/mkdocs/docs/api/macros/json_no_simd.md @@ -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.md) - the zero-copy view, whose parser the macro also affects +- [Zero-copy JSON views](../../features/json_view/index.md) - the zero-copy view, whose parser the macro also affects ## Version history diff --git a/docs/mkdocs/docs/api/ordered_json_document.md b/docs/mkdocs/docs/api/ordered_json_document.md index 23bde281a..3edc2bce8 100644 --- a/docs/mkdocs/docs/api/ordered_json_document.md +++ b/docs/mkdocs/docs/api/ordered_json_document.md @@ -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.md) - how to parse, inspect and edit a document without building a tree +- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree ## Version history diff --git a/docs/mkdocs/docs/api/ordered_json_editable_document.md b/docs/mkdocs/docs/api/ordered_json_editable_document.md index de0d041ab..3073f0171 100644 --- a/docs/mkdocs/docs/api/ordered_json_editable_document.md +++ b/docs/mkdocs/docs/api/ordered_json_editable_document.md @@ -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.md#editing-a-document) - editing a document in place and writing it back +- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back ## Version history diff --git a/docs/mkdocs/docs/api/ordered_json_editable_view.md b/docs/mkdocs/docs/api/ordered_json_editable_view.md index e87cb9bb5..a2975ac17 100644 --- a/docs/mkdocs/docs/api/ordered_json_editable_view.md +++ b/docs/mkdocs/docs/api/ordered_json_editable_view.md @@ -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.md#editing-a-document) - editing a document in place and writing it back +- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back ## Version history diff --git a/docs/mkdocs/docs/api/ordered_json_view.md b/docs/mkdocs/docs/api/ordered_json_view.md index 7ca5356d6..3d4948df6 100644 --- a/docs/mkdocs/docs/api/ordered_json_view.md +++ b/docs/mkdocs/docs/api/ordered_json_view.md @@ -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.md) - how to parse, inspect and edit a document without building a tree +- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree ## Version history diff --git a/docs/mkdocs/docs/api/view_mode.md b/docs/mkdocs/docs/api/view_mode.md index c754c7ed4..7629c85f7 100644 --- a/docs/mkdocs/docs/api/view_mode.md +++ b/docs/mkdocs/docs/api/view_mode.md @@ -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.md#writing-generic-code) - functions that accept views +- [Writing generic code](../features/json_view/generic_code.md) - functions that accept views ## Version history diff --git a/docs/mkdocs/docs/css/fig-jsonview.css b/docs/mkdocs/docs/css/fig-jsonview.css index 89cd3bcc9..6044b9115 100644 --- a/docs/mkdocs/docs/css/fig-jsonview.css +++ b/docs/mkdocs/docs/css/fig-jsonview.css @@ -1,4 +1,4 @@ -/* figures: jsonview (features/json_view.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */ +/* figures: jsonview (features/json_view/index.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 */ diff --git a/docs/mkdocs/docs/features/conversions.md b/docs/mkdocs/docs/features/conversions.md index 69c2de834..3c75519c9 100644 --- a/docs/mkdocs/docs/features/conversions.md +++ b/docs/mkdocs/docs/features/conversions.md @@ -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.md#getting-values-out-without-copying). + [Zero-copy JSON views](json_view/reading.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.md) - get values out of a document or a view +- [Zero-copy JSON views](json_view/index.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 diff --git a/docs/mkdocs/docs/features/creating_values.md b/docs/mkdocs/docs/features/creating_values.md index d1ae587fc..b150baece 100644 --- a/docs/mkdocs/docs/features/creating_values.md +++ b/docs/mkdocs/docs/features/creating_values.md @@ -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.md). +into a value; see [Zero-copy JSON views](json_view/index.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.md) - create a document and a view from JSON text instead of a value +- [Zero-copy JSON views](json_view/index.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 diff --git a/docs/mkdocs/docs/features/index.md b/docs/mkdocs/docs/features/index.md index f9b0f20cc..81427b3af 100644 --- a/docs/mkdocs/docs/features/index.md +++ b/docs/mkdocs/docs/features/index.md @@ -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.md) — read a JSON text through a flat index instead of building a `json` tree; +- [Zero-copy JSON views](json_view/index.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.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. +- [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. - [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar. ## Accessing and modifying values diff --git a/docs/mkdocs/docs/features/json_view.md b/docs/mkdocs/docs/features/json_view.md deleted file mode 100644 index 44bf581a5..000000000 --- a/docs/mkdocs/docs/features/json_view.md +++ /dev/null @@ -1,589 +0,0 @@ -# Zero-copy JSON views - -`#!cpp ` 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: - -
---8<-- "images/json_view_layout.svg" -
- -

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.

- -## How to use it - -Include `` in addition to (or instead of) ``. 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. - -
---8<-- "images/json_view_lifetime.svg" -
- -

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.

- -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 ``. 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` 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()`](../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such as -the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get()` of -`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get()` reads `#!cpp 2`. - -**What stays the same as `parse()`?** [`materialize()`](../api/basic_json_view/materialize.md) and -[`get>()`](../api/basic_json_view/get.md) replay every member in order, so they keep the *last* value -exactly like `#!cpp basic_json::parse()` (at the position of the first occurrence of the key, for an -[`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()`](../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()` 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()`) 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()` (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(); // 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()`, 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`, and -[`json_editable_view`](../api/json_editable_view.md) is `#!cpp basic_json_view`. 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 -void f(basic_json_view v); - -// views of read-only documents only -template -void f(basic_json_view v); - -// views of either document type -template -void f(basic_json_view 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`](../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()`](../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()`. - -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::value || std::is_same::value, int >::type = 0 > -void from_json(const J& j, person& p) -{ - p.name = j.at("name").template get(); - p.age = j.at("age").template get(); -} -``` - -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()`/`get_to()`. An unconstrained -`#!cpp template 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()` 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`](../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: - -
-

json

keys sorted, numbers rewritten

{"host":"db2",
- "name":"cache",
- "port":6379,
- "price":19.9,
- "retries":3}
-

ordered_json

key order kept, numbers rewritten

{"name":"cache",
- "host":"db2",
- "port":6379,
- "price":19.9,
- "retries":3}
-

json_editable_document

order and spelling kept

{"name":"cache",
- "host":"db2",
- "port":6379,
- "price":19.90,
- "retries":3}
-
- -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)): - -
-
-
---8<-- "images/json_view_edit_0.svg" -
-
Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.
-
-
-
---8<-- "images/json_view_edit_1.svg" -
-
Step 2 of 5. doc.set(doc.root(), "port", 6380) overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.
-
-
-
---8<-- "images/json_view_edit_2.svg" -
-
Step 3 of 5. doc.erase(doc.root()["tags"], 0): the array is edited for the first time, so its elements move to a block of links in owned storage. The node of "eu" stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.
-
-
-
---8<-- "images/json_view_edit_3.svg" -
-
Step 4 of 5. doc.push_back(doc.root()["tags"], "new"): 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.
-
-
-
---8<-- "images/json_view_edit_4.svg" -
-
Step 5 of 5. doc.set(doc.root(), "retries", 3): 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.
-
-
- -

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.

- -## 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 | - -

Each column is one way to work with JSON in this library, and each row compares one property of them.

- -## 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. diff --git a/docs/mkdocs/docs/features/json_view/differences.md b/docs/mkdocs/docs/features/json_view/differences.md new file mode 100644 index 000000000..e763e70fd --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/differences.md @@ -0,0 +1,219 @@ +# 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()`, implicit conversions, `get_ptr()`, `get_ref()` | `get()`, `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()` | + +

Each row is one difference; the last column names the way to get `basic_json`'s behavior from a view, where there is one.

+ +## 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 ``. 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` 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()`](../../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such +as the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get()` of +`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get()` reads `#!cpp 2`. + +**What stays the same as `parse()`?** [`materialize()`](../../api/basic_json_view/materialize.md) and +[`get>()`](../../api/basic_json_view/get.md) (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()`](../../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()`](../../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. diff --git a/docs/mkdocs/docs/features/json_view/editing.md b/docs/mkdocs/docs/features/json_view/editing.md new file mode 100644 index 000000000..12fcd7d84 --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/editing.md @@ -0,0 +1,133 @@ +# 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`](../../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: + +
+

json

keys sorted, numbers rewritten

{"host":"db2",
+ "name":"cache",
+ "port":6379,
+ "price":19.9,
+ "retries":3}
+

ordered_json

key order kept, numbers rewritten

{"name":"cache",
+ "host":"db2",
+ "port":6379,
+ "price":19.9,
+ "retries":3}
+

json_editable_document

order and spelling kept

{"name":"cache",
+ "host":"db2",
+ "port":6379,
+ "price":19.90,
+ "retries":3}
+
+ +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)): + +
+
+
+--8<-- "images/json_view_edit_0.svg" +
+
Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.
+
+
+
+--8<-- "images/json_view_edit_1.svg" +
+
Step 2 of 5. doc.set(doc.root(), "port", 6380) overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.
+
+
+
+--8<-- "images/json_view_edit_2.svg" +
+
Step 3 of 5. doc.erase(doc.root()["tags"], 0): the array is edited for the first time, so its elements move to a block of links in owned storage. The node of "eu" stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.
+
+
+
+--8<-- "images/json_view_edit_3.svg" +
+
Step 4 of 5. doc.push_back(doc.root()["tags"], "new"): 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.
+
+
+
+--8<-- "images/json_view_edit_4.svg" +
+
Step 5 of 5. doc.set(doc.root(), "retries", 3): 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.
+
+
+ +

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.

+ +## 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. diff --git a/docs/mkdocs/docs/features/json_view/generic_code.md b/docs/mkdocs/docs/features/json_view/generic_code.md new file mode 100644 index 000000000..a06178d05 --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/generic_code.md @@ -0,0 +1,95 @@ +# 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`, and +[`json_editable_view`](../../api/json_editable_view.md) is `#!cpp basic_json_view`. 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 +void f(basic_json_view v); + +// views of read-only documents only +template +void f(basic_json_view v); + +// views of either document type +template +void f(basic_json_view 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`](../../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()`](../../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()`. + +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::value || std::is_same::value, int >::type = 0 > +void from_json(const J& j, person& p) +{ + p.name = j.at("name").template get(); + p.age = j.at("age").template get(); +} +``` + +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()`/`get_to()`. An unconstrained +`#!cpp template 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()` 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()` converts without materializing + +## Version history + +- Added in version 3.13.0. diff --git a/docs/mkdocs/docs/features/json_view/images.md b/docs/mkdocs/docs/features/json_view/images.md new file mode 100644 index 000000000..cb83295e8 --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/images.md @@ -0,0 +1,70 @@ +# 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. diff --git a/docs/mkdocs/docs/features/json_view/index.md b/docs/mkdocs/docs/features/json_view/index.md new file mode 100644 index 000000000..04bb1db63 --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/index.md @@ -0,0 +1,106 @@ +# Zero-copy JSON views + +`#!cpp ` 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: + +
+--8<-- "images/json_view_layout.svg" +
+ +

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.

+ +## How to use it + +Include `` in addition to (or instead of) ``. 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()`, 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 | + +

Each column is one way to work with JSON in this library, and each row compares one property of them.

+ +## 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. diff --git a/docs/mkdocs/docs/features/json_view/lifetime.md b/docs/mkdocs/docs/features/json_view/lifetime.md new file mode 100644 index 000000000..2ecc8b711 --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/lifetime.md @@ -0,0 +1,73 @@ +# 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). + +
+--8<-- "images/json_view_lifetime.svg" +
+ +

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.

+ +## 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. diff --git a/docs/mkdocs/docs/features/json_view/reading.md b/docs/mkdocs/docs/features/json_view/reading.md new file mode 100644 index 000000000..9040e85e4 --- /dev/null +++ b/docs/mkdocs/docs/features/json_view/reading.md @@ -0,0 +1,87 @@ +# Reading values + +## Getting values out without copying + +[`get()`](../../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()` 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()`) 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()` (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(); // 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()`, 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. diff --git a/docs/mkdocs/docs/features/modifying_values.md b/docs/mkdocs/docs/features/modifying_values.md index 255a62d8b..074ca0b82 100644 --- a/docs/mkdocs/docs/features/modifying_values.md +++ b/docs/mkdocs/docs/features/modifying_values.md @@ -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.md#editing-a-document) can also be changed in place, without building a `basic_json` tree for the +[document](json_view/editing.md) 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.md#editing-a-document) - change a parsed document in place +- [Editing a document](json_view/editing.md) - 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 diff --git a/docs/mkdocs/docs/features/parsing/index.md b/docs/mkdocs/docs/features/parsing/index.md index 901626952..be6680f4f 100644 --- a/docs/mkdocs/docs/features/parsing/index.md +++ b/docs/mkdocs/docs/features/parsing/index.md @@ -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.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/index.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() << '\n'; ``` -See [Zero-copy JSON views](../json_view.md) for the concept and the trade-offs. +See [Zero-copy JSON views](../json_view/index.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.md) - parse into a read-only document and read it through views +- [Zero-copy JSON views](../json_view/index.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 diff --git a/docs/mkdocs/docs/features/parsing/large_and_partial_input.md b/docs/mkdocs/docs/features/parsing/large_and_partial_input.md index 89ce90bf8..b5847455c 100644 --- a/docs/mkdocs/docs/features/parsing/large_and_partial_input.md +++ b/docs/mkdocs/docs/features/parsing/large_and_partial_input.md @@ -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.md) from +If the whole text is available and you only need to read some of it, a [`json_document`](../json_view/index.md) from `` (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.md) for details and for the lifetime rules. +[Zero-copy JSON views](../json_view/index.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.md) - read parts of a large document without building a tree +- [Zero-copy JSON views](../json_view/index.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 diff --git a/docs/mkdocs/docs/features/performance.md b/docs/mkdocs/docs/features/performance.md index ffea9c215..ac73b478b 100644 --- a/docs/mkdocs/docs/features/performance.md +++ b/docs/mkdocs/docs/features/performance.md @@ -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.md) (`#!cpp `) instead of a `basic_json`. It keeps +- Parse into a [`json_document`](json_view/index.md) (`#!cpp `) 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.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view). + [choosing between `json`, the SAX interface, and `json_view`](json_view/index.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.md) +- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view/index.md) (`#!cpp `, 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.md). + outlive it; see [Zero-copy JSON views](json_view/index.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.md) - parse into a flat index of the text and read it 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 - [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 diff --git a/docs/mkdocs/docs/home/architecture.md b/docs/mkdocs/docs/home/architecture.md index 1b0d84dd7..9e94e5425 100644 --- a/docs/mkdocs/docs/home/architecture.md +++ b/docs/mkdocs/docs/home/architecture.md @@ -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.md). It builds on `json.hpp` internals (it requires the + JSON text; see [Zero-copy JSON views](../features/json_view/index.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.md)) +A [`basic_json_document`](../api/basic_json_document/index.md) (see [Zero-copy JSON views](../features/json_view/index.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.md) - the flat node index that backs views and documents +- [Zero-copy JSON views](../features/json_view/index.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 diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 8d4473cb0..92e16b535 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -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.md#images): either the bytes are not one [`save()`](../api/basic_json_document/save.md) +[image](../features/json_view/images.md): 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.md#images). + This exception was added in version 3.13.0, together with [images](../features/json_view/images.md). ## 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.md). + This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view/index.md). ### json.exception.type_error.320 [`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an -[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document. +[image](../features/json_view/images.md) 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.md#images). + This exception was added in version 3.13.0, together with [images](../features/json_view/images.md). ### 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.md#images): [`save()`](../api/basic_json_document/save.md) throws it if the node +[image](../features/json_view/images.md): [`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 [``](../features/json_view.md). + This exception was added in version 3.13.0, together with [``](../features/json_view/index.md). [`basic_json::parse()`](../api/basic_json/parse.md) has no such limit. ## Further exceptions diff --git a/docs/mkdocs/docs/home/faq.md b/docs/mkdocs/docs/home/faq.md index c43acef37..efe7acb7f 100644 --- a/docs/mkdocs/docs/home/faq.md +++ b/docs/mkdocs/docs/home/faq.md @@ -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.md) (added in version 3.13.0). See +[`json_document`](../features/json_view/index.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 diff --git a/docs/mkdocs/docs/integration/index.md b/docs/mkdocs/docs/integration/index.md index dd73ae500..da53fb036 100644 --- a/docs/mkdocs/docs/integration/index.md +++ b/docs/mkdocs/docs/integration/index.md @@ -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 ` works. Download the companion headers listed below only if you use them: `json_view.hpp` for the - [zero-copy views](../features/json_view.md), `json_fwd.hpp` for forward declarations, and `json_literals.hpp` if + [zero-copy views](../features/json_view/index.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.md). +see [Zero-copy JSON views](../features/json_view/index.md). ## Notes for builds and distribution diff --git a/docs/mkdocs/docs/js/fig-jsonview.js b/docs/mkdocs/docs/js/fig-jsonview.js index f5f76f5f2..c49d3880e 100644 --- a/docs/mkdocs/docs/js/fig-jsonview.js +++ b/docs/mkdocs/docs/js/fig-jsonview.js @@ -1,4 +1,4 @@ -/* figures: jsonview -- stepper for the edit frames on features/json_view.md. +/* figures: jsonview -- stepper for the edit frames on features/json_view/index.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 () { diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 08c46ff83..4d167ed68 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -157,7 +157,14 @@ nav: - features/types/index.md - features/types/number_handling.md - features/types/template_parameters.md - - features/json_view.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 - Integration: - integration/index.md - integration/migration_guide.md