mirror of
https://github.com/nlohmann/json.git
synced 2026-11-09 22:12:32 +00:00
deploy: 6f01030c4e
This commit is contained in:
1 parent
cf8df97ba7
commit
eeea067a72
566 files changed
+15370
-2688
No files matched your search
@@ -0,0 +1,220 @@
|
||||
# Differences from `basic_json`
|
||||
|
||||
A [`basic_json_view`](../../api/basic_json_view/index.md) offers the read-only part of the
|
||||
[`basic_json`](../../api/basic_json/index.md) interface, and in most respects it behaves exactly like the value
|
||||
[`basic_json::parse()`](../../api/basic_json/parse.md) would have built from the same text. This page lists where it
|
||||
does not: what a document parses differently, what a view's functions do differently, and which parts of the
|
||||
`basic_json` interface a view does not have.
|
||||
|
||||
## Summary
|
||||
|
||||
| | `basic_json` value from `parse()` | view of a `json_document` | to get the `basic_json` behavior |
|
||||
|---|---|---|---|
|
||||
| [Integer types](#parsing) | any `number_integer_t`/`number_unsigned_t` | 64-bit only (compile-time check) | — |
|
||||
| [Input size](#parsing) | unlimited | less than 4 GiB | — |
|
||||
| [Parser callbacks](#parsing) | `parser_callback_t` | none | filter while iterating |
|
||||
| [`operator[]` on a missing key or index](#element-access) | inserts (non-`const`) or undefined behavior (`const`) | returns a discarded view | — |
|
||||
| [Object member order](#object-member-order) | sorted by key (`json`), insertion order (`ordered_json`) | document order | `materialize()` |
|
||||
| [Repeated keys](#duplicate-keys) | the last value is kept | lookups find the first member; iteration, `size()`, and `dump()` see all of them | `materialize()` |
|
||||
| [Conversions](#conversions) | `get<T>()`, implicit conversions, `get_ptr()`, `get_ref()` | `get<T>()`, `get_string()`, `number_token()` | `materialize()` |
|
||||
| [Number spelling](#serialization) | lost when parsing | kept: `number_token()`, `number_format::source` | — |
|
||||
| [Comparison](#comparison) | `==`, `!=`, `<`, `<=>`, ... | `==` and `!=` only | `materialize()` |
|
||||
| [Modification](#what-a-view-does-not-have) | member functions of the value | only through an [editable document](editing.md) | `materialize()` |
|
||||
| [Iterators](#what-a-view-does-not-have) | bidirectional (`rbegin()`/`rend()`) | forward only | `materialize()` |
|
||||
|
||||
<p class="nj-table-caption" markdown>Each row is one difference; the last column names the way to get `basic_json`'s behavior from a view, where there is one.</p>
|
||||
|
||||
## Parsing
|
||||
|
||||
### What is the same
|
||||
|
||||
- **Accept/reject.** [`accept()`](../../api/basic_json_document/accept.md) and
|
||||
[`parse()`](../../api/basic_json_document/parse.md) accept and reject exactly the same inputs as
|
||||
[`basic_json::accept()`](../../api/basic_json/accept.md)/[`basic_json::parse()`](../../api/basic_json/parse.md),
|
||||
with the same `ignore_comments` and `ignore_trailing_commas` options.
|
||||
- **Errors.** A failing parse throws the same exception — the same id, message, and position — because on failure
|
||||
the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and
|
||||
the replay builds no values: it costs about a lexing pass up to the error.
|
||||
`#!cpp allow_exceptions == false` skips the replay and gives a
|
||||
[discarded](../../api/basic_json_document/is_discarded.md) document instead of throwing, just as it gives a
|
||||
discarded value for `#!cpp basic_json::parse()`.
|
||||
- **Number classification.** An integer literal that does not fit into the 64-bit integer type becomes a
|
||||
floating-point value, exactly as it does for `#!cpp basic_json::parse()`.
|
||||
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](../../api/macros/json_strict_nul_handling.md) and
|
||||
[`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md)/[`JSON_THROW_USER`](../../api/macros/json_throw_user.md)
|
||||
behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or
|
||||
how it reports errors. The other macros only affect the values `materialize()` produces, through `BasicJsonType`.
|
||||
Two of them also add context to the exceptions of views, see [Exception context](#exception-context).
|
||||
|
||||
### What is different
|
||||
|
||||
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and
|
||||
`number_unsigned_t` to both be 64 bits wide; this is a compile-time `#!cpp static_assert`.
|
||||
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws
|
||||
[`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416), a limit
|
||||
`#!cpp basic_json::parse()` does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits
|
||||
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
||||
end must still fit.
|
||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
||||
is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a
|
||||
`#!cpp std::string` yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and
|
||||
was about 6 to 12 % faster than parsing from an `#!cpp std::ifstream`.
|
||||
- **No parser callbacks.** [`parse()`](../../api/basic_json_document/parse.md) takes no
|
||||
[`parser_callback_t`](../../api/basic_json/parser_callback_t.md): a document always indexes the whole text. Skip
|
||||
the values you do not need when reading the view instead.
|
||||
- **JSON text only.** A document is parsed from JSON text; there is no counterpart of
|
||||
[`from_cbor()`](../../api/basic_json/from_cbor.md) and the other binary formats, and
|
||||
[`is_binary()`](../../api/basic_json_view/is_binary.md) is always `#!cpp false`.
|
||||
|
||||
## Element access
|
||||
|
||||
**Chained access is safe.** [`operator[]`](../../api/basic_json_view/operator%5B%5D.md) with a missing key, an index
|
||||
out of range, or an unresolvable JSON pointer returns a [discarded](../../api/basic_json_view/is_discarded.md) view,
|
||||
and `operator[]` on a discarded view returns a discarded view without throwing: `#!cpp v["a"]["b"][0]` can be tested
|
||||
once at the end. Type errors on values that exist (a key on an array, an index on an object) still throw, and
|
||||
[`at`](../../api/basic_json_view/at.md) throws for every missing value.
|
||||
|
||||
`basic_json`'s non-`const` `operator[]` instead inserts a missing key, and its `const` `operator[]` has undefined
|
||||
behavior for one.
|
||||
|
||||
## Objects
|
||||
|
||||
### Object member order
|
||||
|
||||
[`begin()`](../../api/basic_json_view/begin.md)/[`end()`](../../api/basic_json_view/end.md) and
|
||||
[`items()`](../../api/basic_json_view/items.md) visit an object's members in the order they appear in the source text.
|
||||
`basic_json`'s default `object_t` is a `std::map`, which sorts by key, so iterating a
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md)d value can print members in a different order than
|
||||
iterating the view they came from. [`dump()`](../../api/basic_json_view/dump.md) of a view writes members in document
|
||||
order as well.
|
||||
|
||||
### Duplicate keys
|
||||
|
||||
!!! warning "A lookup in a view and in the parsed value can give different answers"
|
||||
|
||||
If an object in the source text repeats a key, [`operator[]`](../../api/basic_json_view/operator%5B%5D.md),
|
||||
[`at`](../../api/basic_json_view/at.md), [`find`](../../api/basic_json_view/find.md),
|
||||
[`value`](../../api/basic_json_view/value.md), [`contains`](../../api/basic_json_view/contains.md), and JSON
|
||||
pointer resolution all return the **first** member with that key. `basic_json::parse()` instead keeps the **last**
|
||||
value of a repeated key, so for `#!cpp {"a":1,"a":2}`, `#!cpp view["a"]` is `#!cpp 1` while
|
||||
`#!cpp parse(text)["a"]` is `#!cpp 2`. [`count`](../../api/basic_json_view/count.md) counts every member with the
|
||||
key, so it is `#!cpp 2` here.
|
||||
|
||||
[`begin()`](../../api/basic_json_view/begin.md)/[`end()`](../../api/basic_json_view/end.md) and
|
||||
[`items()`](../../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
||||
[`size()`](../../api/basic_json_view/size.md) counts all of them, so `#!cpp view.size()` can be larger than
|
||||
`#!cpp view.materialize().size()`. [`dump()`](../../api/basic_json_view/dump.md) writes every occurrence, too, so
|
||||
`#!cpp materialize().dump()` gives a different result for such an object.
|
||||
|
||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
||||
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
||||
objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first
|
||||
member of a key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return
|
||||
the first member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver
|
||||
that sees duplicates is unpredictable, so neither choice is wrong.
|
||||
|
||||
The same holds for [`get<T>()`](../../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such
|
||||
as the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get<point>()` of
|
||||
`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get<point>()` reads `#!cpp 2`.
|
||||
|
||||
**What stays the same as `parse()`?** [`materialize()`](../../api/basic_json_view/materialize.md) and
|
||||
[`get<std::map<...>>()`](../../api/basic_json_view/get.md) (and the other containers built from an object) replay
|
||||
every member in order, so they keep the *last* value exactly like `#!cpp basic_json::parse()` (at the position of the
|
||||
first occurrence of the key, for an [`ordered_json`](../../api/ordered_json.md)).
|
||||
|
||||
**How do I get the value `parse()` would give?** Either call `#!cpp view.materialize()` and look the key up in the
|
||||
result, or iterate the members and keep the last match:
|
||||
|
||||
```cpp
|
||||
// the last member with the key "a", as parse() would keep it
|
||||
json_view last;
|
||||
for (const auto item : view.items())
|
||||
{
|
||||
if (item.key() == "a")
|
||||
{
|
||||
last = item.value();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you cannot trust the source text to have unique keys, check [`size()`](../../api/basic_json_view/size.md) against
|
||||
the number of distinct keys, or reject duplicates when iterating.
|
||||
|
||||
## Conversions
|
||||
|
||||
- **No implicit conversions.** A view has no `#!cpp operator ValueType()`, no `#!cpp get_ptr()`, and no
|
||||
`#!cpp get_ref()`; use [`get<T>()`](../../api/basic_json_view/get.md), or
|
||||
[`get_string()`](../../api/basic_json_view/get_string.md) for a string without a copy.
|
||||
- **`from_json()` overloads.** [`get<T>()`](../../api/basic_json_view/get.md) calls `T`'s `from_json()` with the view
|
||||
only if that `from_json()` accepts views, and with [`materialize()`](../../api/basic_json_view/materialize.md)
|
||||
otherwise; see [A `from_json()` that reads views](generic_code.md#a-from_json-that-reads-views). Both give the same
|
||||
result except for [repeated keys](#duplicate-keys).
|
||||
- **`string_view` members.** A struct with a `#!cpp std::string_view` member decoded from a view points into the
|
||||
document, see [`string_view` members](reading.md#string_view-members). Decoding the same struct from a `json` value
|
||||
compiles, too, but dangles at once if the value is a temporary.
|
||||
|
||||
## Serialization
|
||||
|
||||
[`dump()`](../../api/basic_json_view/dump.md) writes numbers the way
|
||||
[`basic_json::dump()`](../../api/basic_json/dump.md) would by default, but writes object members in
|
||||
[document order](#object-member-order) and writes [every occurrence of a repeated key](#duplicate-keys).
|
||||
|
||||
A view can also do what a `basic_json` value cannot: [`number_token()`](../../api/basic_json_view/number_token.md)
|
||||
and [`number_format::source`](../../api/basic_json_view/number_format.md) give every number exactly as it was spelled
|
||||
in the source text, while parsing into `basic_json` already reduces a number to its `#!cpp double`/`#!cpp int64_t`
|
||||
value. See [Writing a view back](reading.md#writing-a-view-back).
|
||||
|
||||
## Comparison
|
||||
|
||||
**Ordering comparisons are not provided** by `basic_json_view` — there is no `#!cpp operator<`.
|
||||
[`operator==`](../../api/basic_json_view/operator_eq.md) and [`operator!=`](../../api/basic_json_view/operator_ne.md)
|
||||
are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) or [`parse()`](../../api/basic_json/parse.md) would produce
|
||||
equal values for them, without ever building a tree to do it. For ordering,
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
||||
|
||||
## Exception context
|
||||
|
||||
With [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md), an exception of a view names the JSON Pointer path of
|
||||
the value within the document, and with
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md), it names the bytes of the value in the
|
||||
source text, as they do for the exceptions of `#!cpp basic_json`.
|
||||
|
||||
A view has neither parent pointers nor stored positions, so both are found from the document's index when the
|
||||
exception is thrown. This takes time linear in the size of the document. One exception has neither: `key()` of an
|
||||
iterator over an array or a primitive value
|
||||
([`invalid_iterator.207`](../../home/exceptions.md#jsonexceptioninvalid_iterator207)), because a view's iterator does
|
||||
not know the value it iterates over.
|
||||
|
||||
**Fewer source positions after edits.** With
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) enabled,
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`. In
|
||||
an [editable document](editing.md), a value written by an edit has none, and neither has an array or object whose
|
||||
elements changed or whose last element has none; a `basic_json` value keeps its positions when it is changed. See
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md#notes).
|
||||
|
||||
## What a view does not have
|
||||
|
||||
A view is a read-only handle. These parts of the `basic_json` interface have no counterpart on a view;
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md) the subtree to use them:
|
||||
|
||||
- **Modifiers** such as `push_back`, `insert`, `erase`, `update`, or `operator=`. A view never changes a value
|
||||
itself; an [editable document](editing.md) changes the values its views refer to through
|
||||
[`set`](../../api/basic_json_document/set.md), [`push_back`](../../api/basic_json_document/push_back.md),
|
||||
[`insert`](../../api/basic_json_document/insert.md), and [`erase`](../../api/basic_json_document/erase.md).
|
||||
- **Reverse iteration.** A view's iterator is a forward iterator; there is no `rbegin()`/`rend()`.
|
||||
- **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.
|
||||
@@ -0,0 +1,10 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/differences/" rel="canonical"><link href=../lifetime/ rel=prev><link href=../reading/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Differences from basic_json - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#differences-from-basic_json class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Differences from basic_json </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg Line truncated
|
||||
<span class=n>json_view</span><span class=w> </span><span class=n>last</span><span class=p>;</span>
|
||||
<span class=k>for</span><span class=w> </span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=k>auto</span><span class=w> </span><span class=n>item</span><span class=w> </span><span class=o>:</span><span class=w> </span><span class=n>view</span><span class=p>.</span><span class=n>items</span><span class=p>())</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=k>if</span><span class=w> </span><span class=p>(</span><span class=n>item</span><span class=p>.</span><span class=n>key</span><span class=p>()</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=s>"a"</span><span class=p>)</span>
|
||||
<span class=w> </span><span class=p>{</span>
|
||||
<span class=w> </span><span class=n>last</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>item</span><span class=p>.</span><span class=n>value</span><span class=p>();</span>
|
||||
<span class=w> </span><span class=p>}</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>If you cannot trust the source text to have unique keys, check <a href=../../../api/basic_json_view/size/ ><code>size()</code></a> against the number of distinct keys, or reject duplicates when iterating.</p> <h2 id=conversions>Conversions<a class=headerlink href=#conversions title="Permanent link">¶</a></h2> <ul> <li><strong>No implicit conversions.</strong> A view has no <code class=highlight><span class=k>operator</span><span class=w> </span><span class=n>ValueType</span><span class=p>()</span></code>, no <code class=highlight><span class=n>get_ptr</span><span class=p>()</span></code>, and no <code class=highlight><span class=n>get_ref</span><span class=p>()</span></code>; use <a href=../../../api/basic_json_view/get/ ><code>get<T>()</code></a>, or <a href=../../../api/basic_json_view/get_string/ ><code>get_string()</code></a> for a string without a copy.</li> <li><strong><code>from_json()</code> overloads.</strong> <a href=../../../api/basic_json_view/get/ ><code>get<T>()</code></a> calls <code>T</code>'s <code>from_json()</code> with the view only if that <code>from_json()</code> accepts views, and with <a href=../../../api/basic_json_view/materialize/ ><code>materialize()</code></a> otherwise; see <a href=../generic_code/#a-from_json-that-reads-views>A <code>from_json()</code> that reads views</a>. Both give the same result except for <a href=#duplicate-keys>repeated keys</a>.</li> <li><strong><code>string_view</code> members.</strong> A struct with a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code> member decoded from a view points into the document, see <a href=../reading/#string_view-members><code>string_view</code> members</a>. Decoding the same struct from a <code>json</code> value compiles, too, but dangles at once if the value is a temporary.</li> </ul> <h2 id=serialization>Serialization<a class=headerlink href=#serialization title="Permanent link">¶</a></h2> <p><a href=../../../api/basic_json_view/dump/ ><code>dump()</code></a> writes numbers the way <a href=../../../api/basic_json/dump/ ><code>basic_json::dump()</code></a> would by default, but writes object members in <a href=#object-member-order>document order</a> and writes <a href=#duplicate-keys>every occurrence of a repeated key</a>.</p> <p>A view can also do what a <code>basic_json</code> value cannot: <a href=../../../api/basic_json_view/number_token/ ><code>number_token()</code></a> and <a href=../../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> give every number exactly as it was spelled in the source text, while parsing into <code>basic_json</code> already reduces a number to its <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> value. See <a href=../reading/#writing-a-view-back>Writing a view back</a>.</p> <h2 id=comparison>Comparison<a class=headerlink href=#comparison title="Permanent link">¶</a></h2> <p><strong>Ordering comparisons are not provided</strong> by <code>basic_json_view</code> — there is no <code class=highlight><span class=k>operator</span><span class=o><</span></code>. <a href=../../../api/basic_json_view/operator_eq/ ><code>operator==</code></a> and <a href=../../../api/basic_json_view/operator_ne/ ><code>operator!=</code></a> are provided, though: two views, or a view and a <code>BasicJsonType</code> value, compare equal exactly when <a href=../../../api/basic_json_view/materialize/ ><code>materialize()</code></a> or <a href=../../../api/basic_json/parse/ ><code>parse()</code></a> would produce equal values for them, without ever building a tree to do it. For ordering, <a href=../../../api/basic_json_view/materialize/ ><code>materialize()</code></a> is the way to get a value you can compare.</p> <h2 id=exception-context>Exception context<a class=headerlink href=#exception-context title="Permanent link">¶</a></h2> <p>With <a href=../../../api/macros/json_diagnostics/ ><code>JSON_DIAGNOSTICS</code></a>, an exception of a view names the <abbr title="JavaScript Object Notation">JSON</abbr> Pointer path of the value within the document, and with <a href=../../../api/macros/json_diagnostic_positions/ ><code>JSON_DIAGNOSTIC_POSITIONS</code></a>, it names the bytes of the value in the source text, as they do for the exceptions of <code class=highlight><span class=n>basic_json</span></code>.</p> <p>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: <code>key()</code> of an iterator over an array or a primitive value (<a href=../../../home/exceptions/#jsonexceptioninvalid_iterator207><code>invalid_iterator.207</code></a>), because a view's iterator does not know the value it iterates over.</p> <p><strong>Fewer source positions after edits.</strong> WLine truncated
|
||||
@@ -0,0 +1,123 @@
|
||||
# Differences from `basic_json`
|
||||
|
||||
A [`basic_json_view`](https://json.nlohmann.me/api/basic_json_view/index.md) offers the read-only part of the [`basic_json`](https://json.nlohmann.me/api/basic_json/index.md) interface, and in most respects it behaves exactly like the value [`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would have built from the same text. This page lists where it does not: what a document parses differently, what a view's functions do differently, and which parts of the `basic_json` interface a view does not have.
|
||||
|
||||
## Summary
|
||||
|
||||
| | `basic_json` value from `parse()` | view of a `json_document` | to get the `basic_json` behavior |
|
||||
| --------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------- |
|
||||
| [Integer types](#parsing) | any `number_integer_t`/`number_unsigned_t` | 64-bit only (compile-time check) | — |
|
||||
| [Input size](#parsing) | unlimited | less than 4 GiB | — |
|
||||
| [Parser callbacks](#parsing) | `parser_callback_t` | none | filter while iterating |
|
||||
| [`operator[]` on a missing key or index](#element-access) | inserts (non-`const`) or undefined behavior (`const`) | returns a discarded view | — |
|
||||
| [Object member order](#object-member-order) | sorted by key (`json`), insertion order (`ordered_json`) | document order | `materialize()` |
|
||||
| [Repeated keys](#duplicate-keys) | the last value is kept | lookups find the first member; iteration, `size()`, and `dump()` see all of them | `materialize()` |
|
||||
| [Conversions](#conversions) | `get<T>()`, implicit conversions, `get_ptr()`, `get_ref()` | `get<T>()`, `get_string()`, `number_token()` | `materialize()` |
|
||||
| [Number spelling](#serialization) | lost when parsing | kept: `number_token()`, `number_format::source` | — |
|
||||
| [Comparison](#comparison) | `==`, `!=`, `<`, `<=>`, ... | `==` and `!=` only | `materialize()` |
|
||||
| [Modification](#what-a-view-does-not-have) | member functions of the value | only through an [editable document](https://json.nlohmann.me/features/json_view/editing/index.md) | `materialize()` |
|
||||
| [Iterators](#what-a-view-does-not-have) | bidirectional (`rbegin()`/`rend()`) | 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()`](https://json.nlohmann.me/api/basic_json_document/accept/index.md) and [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) accept and reject exactly the same inputs as [`basic_json::accept()`](https://json.nlohmann.me/api/basic_json/accept/index.md)/[`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.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. `allow_exceptions == false` skips the replay and gives a [discarded](https://json.nlohmann.me/api/basic_json_document/is_discarded/index.md) document instead of throwing, just as it gives a discarded value for `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 `basic_json::parse()`.
|
||||
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](https://json.nlohmann.me/api/macros/json_strict_nul_handling/index.md) and [`JSON_NOEXCEPTION`](https://json.nlohmann.me/api/macros/json_noexception/index.md)/[`JSON_THROW_USER`](https://json.nlohmann.me/api/macros/json_throw_user/index.md) behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or how it reports errors. The other macros only affect the values `materialize()` produces, through `BasicJsonType`. Two of them also add context to the exceptions of views, see [Exception context](#exception-context).
|
||||
|
||||
### What is different
|
||||
|
||||
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and `number_unsigned_t` to both be 64 bits wide; this is a compile-time `static_assert`.
|
||||
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws [`out_of_range.416`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range416), a limit `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 `std::istream`: the stream is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a `std::string` yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and was about 6 to 12 % faster than parsing from an `std::ifstream`.
|
||||
- **No parser callbacks.** [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) takes no [`parser_callback_t`](https://json.nlohmann.me/api/basic_json/parser_callback_t/index.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()`](https://json.nlohmann.me/api/basic_json/from_cbor/index.md) and the other binary formats, and [`is_binary()`](https://json.nlohmann.me/api/basic_json_view/is_binary/index.md) is always `false`.
|
||||
|
||||
## Element access
|
||||
|
||||
**Chained access is safe.** [`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md) with a missing key, an index out of range, or an unresolvable JSON pointer returns a [discarded](https://json.nlohmann.me/api/basic_json_view/is_discarded/index.md) view, and `operator[]` on a discarded view returns a discarded view without throwing: `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`](https://json.nlohmann.me/api/basic_json_view/at/index.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()`](https://json.nlohmann.me/api/basic_json_view/begin/index.md)/[`end()`](https://json.nlohmann.me/api/basic_json_view/end/index.md) and [`items()`](https://json.nlohmann.me/api/basic_json_view/items/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d value can print members in a different order than iterating the view they came from. [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of a view writes members in document order as well.
|
||||
|
||||
### Duplicate keys
|
||||
|
||||
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[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md), [`at`](https://json.nlohmann.me/api/basic_json_view/at/index.md), [`find`](https://json.nlohmann.me/api/basic_json_view/find/index.md), [`value`](https://json.nlohmann.me/api/basic_json_view/value/index.md), [`contains`](https://json.nlohmann.me/api/basic_json_view/contains/index.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 `{"a":1,"a":2}`, `view["a"]` is `1` while `parse(text)["a"]` is `2`. [`count`](https://json.nlohmann.me/api/basic_json_view/count/index.md) counts every member with the key, so it is `2` here.
|
||||
|
||||
[`begin()`](https://json.nlohmann.me/api/basic_json_view/begin/index.md)/[`end()`](https://json.nlohmann.me/api/basic_json_view/end/index.md) and [`items()`](https://json.nlohmann.me/api/basic_json_view/items/index.md) visit *every* occurrence, in document order, and [`size()`](https://json.nlohmann.me/api/basic_json_view/size/index.md) counts all of them, so `view.size()` can be larger than `view.materialize().size()`. [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) writes every occurrence, too, so `materialize().dump()` gives a different result for such an object.
|
||||
|
||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup to scan all members of the object, even when the key is found at the very first one: this made lookups in small objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first member of a key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return the first member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees duplicates is unpredictable, so neither choice is wrong.
|
||||
|
||||
The same holds for [`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.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 `get<point>()` of `{"x":1,"x":2}` reads `x == 1`, while `materialize().get<point>()` reads `2`.
|
||||
|
||||
**What stays the same as `parse()`?** [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) and [`get<std::map<...>>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) (and the other containers built from an object) replay every member in order, so they keep the *last* value exactly like `basic_json::parse()` (at the position of the first occurrence of the key, for an [`ordered_json`](https://json.nlohmann.me/api/ordered_json/index.md)).
|
||||
|
||||
**How do I get the value `parse()` would give?** Either call `view.materialize()` and look the key up in the result, or iterate the members and keep the last match:
|
||||
|
||||
```
|
||||
// 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()`](https://json.nlohmann.me/api/basic_json_view/size/index.md) against the number of distinct keys, or reject duplicates when iterating.
|
||||
|
||||
## Conversions
|
||||
|
||||
- **No implicit conversions.** A view has no `operator ValueType()`, no `get_ptr()`, and no `get_ref()`; use [`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md), or [`get_string()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) for a string without a copy.
|
||||
- **`from_json()` overloads.** [`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) calls `T`'s `from_json()` with the view only if that `from_json()` accepts views, and with [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) otherwise; see [A `from_json()` that reads views](https://json.nlohmann.me/features/json_view/generic_code/#a-from_json-that-reads-views). Both give the same result except for [repeated keys](#duplicate-keys).
|
||||
- **`string_view` members.** A struct with a `std::string_view` member decoded from a view points into the document, see [`string_view` members](https://json.nlohmann.me/features/json_view/reading/#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()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) writes numbers the way [`basic_json::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.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()`](https://json.nlohmann.me/api/basic_json_view/number_token/index.md) and [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) give every number exactly as it was spelled in the source text, while parsing into `basic_json` already reduces a number to its `double`/`int64_t` value. See [Writing a view back](https://json.nlohmann.me/features/json_view/reading/#writing-a-view-back).
|
||||
|
||||
## Comparison
|
||||
|
||||
**Ordering comparisons are not provided** by `basic_json_view` — there is no `operator<`. [`operator==`](https://json.nlohmann.me/api/basic_json_view/operator_eq/index.md) and [`operator!=`](https://json.nlohmann.me/api/basic_json_view/operator_ne/index.md) are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) or [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would produce equal values for them, without ever building a tree to do it. For ordering, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) is the way to get a value you can compare.
|
||||
|
||||
## Exception context
|
||||
|
||||
With [`JSON_DIAGNOSTICS`](https://json.nlohmann.me/api/macros/json_diagnostics/index.md), an exception of a view names the JSON Pointer path of the value within the document, and with [`JSON_DIAGNOSTIC_POSITIONS`](https://json.nlohmann.me/api/macros/json_diagnostic_positions/index.md), it names the bytes of the value in the source text, as they do for the exceptions of `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`](https://json.nlohmann.me/home/exceptions/#jsonexceptioninvalid_iterator207)), because a view's iterator does not know the value it iterates over.
|
||||
|
||||
**Fewer source positions after edits.** With [`JSON_DIAGNOSTIC_POSITIONS`](https://json.nlohmann.me/api/macros/json_diagnostic_positions/index.md) enabled, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) sets the same positions as `basic_json::parse()`. In an [editable document](https://json.nlohmann.me/features/json_view/editing/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/#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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.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](https://json.nlohmann.me/features/json_view/editing/index.md) changes the values its views refer to through [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md), [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md), [`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md), and [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md).
|
||||
- **Reverse iteration.** A view's iterator is a forward iterator; there is no `rbegin()`/`rend()`.
|
||||
- **JSON Patch and friends**: [`patch()`](https://json.nlohmann.me/api/basic_json/patch/index.md), [`merge_patch()`](https://json.nlohmann.me/api/basic_json/merge_patch/index.md), [`diff()`](https://json.nlohmann.me/api/basic_json/diff/index.md), [`flatten()`](https://json.nlohmann.me/api/basic_json/flatten/index.md).
|
||||
- **Binary formats**: [`to_cbor()`](https://json.nlohmann.me/api/basic_json/to_cbor/index.md) and the other writers, and binary values ([`get_binary()`](https://json.nlohmann.me/api/basic_json/get_binary/index.md)).
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](https://json.nlohmann.me/features/json_view/index.md) - overview
|
||||
- [`basic_json_view`](https://json.nlohmann.me/api/basic_json_view/index.md) - the full interface of a view
|
||||
- [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) - build the `basic_json` value of a view
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0 unreleased.
|
||||
@@ -0,0 +1,557 @@
|
||||
# Editing a document
|
||||
|
||||
A [`json_document`](../../api/json_document.md)/[`json_view`](../../api/json_view.md) lets you look at a parsed text
|
||||
without copying it, but not change it.
|
||||
[`basic_json_document<BasicJsonType, view_mode::editable>`](../../api/basic_json_document/index.md) — more
|
||||
conveniently spelled [`json_editable_document`](../../api/json_editable_document.md) or
|
||||
[`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) — also lets you
|
||||
[`set`](../../api/basic_json_document/set.md) a value, [`push_back`](../../api/basic_json_document/push_back.md) onto
|
||||
or [`insert`](../../api/basic_json_document/insert.md) into an array, and
|
||||
[`erase`](../../api/basic_json_document/erase.md) an object member or an array element, still without ever building a
|
||||
`basic_json` tree for parts you do not touch.
|
||||
|
||||
The [mode](../../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so
|
||||
`json_document`/`ordered_json_document` are unaffected — they carry none of the bookkeeping edits need, and calling
|
||||
`set`/`push_back`/`insert`/`erase` on one is a compile error, not a runtime one.
|
||||
|
||||
## Why: editing without reformatting
|
||||
|
||||
A price like `#!cpp 19.90` is exactly the kind of value that makes editing a `json` or `ordered_json` value in place
|
||||
lossy. Say you parse a configuration file, patch one field, and write it back:
|
||||
|
||||
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
||||
shortest round-trip form on the way out — a one-field patch turns into a diff that reorders the whole file and
|
||||
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
||||
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
||||
|
||||
The patch of the example below (set `"host"`, add `"retries"`) written back by each of them:
|
||||
|
||||
<div class="nj-jsonview-cmp" role="group" aria-label="The same patch written back by json, ordered_json, and json_editable_document">
|
||||
<div><p class="nj-jsonview-cmp-h"><code>json</code></p><p class="nj-jsonview-cmp-v">keys sorted, numbers rewritten</p><pre>{<span class="nj-jsonview-bad">"host"</span>:"db2",
|
||||
"name":"cache",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||
"retries":3}</pre></div>
|
||||
<div><p class="nj-jsonview-cmp-h"><code>ordered_json</code></p><p class="nj-jsonview-cmp-v">key order kept, numbers rewritten</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-bad">19.9</span>,
|
||||
"retries":3}</pre></div>
|
||||
<div class="nj-jsonview-cmp--best"><p class="nj-jsonview-cmp-h"><code>json_editable_document</code></p><p class="nj-jsonview-cmp-v">order and spelling kept</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class="nj-jsonview-ok">19.90</span>,
|
||||
"retries":3}</pre></div>
|
||||
</div>
|
||||
|
||||
An editable document keeps both. [`dump()`](../../api/basic_json_view/dump.md) of an edited document writes members in
|
||||
document order — a member [`set`](../../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
||||
inserted, and an [`erase`](../../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
||||
keeps its place — and [`number_format::source`](../../api/basic_json_view/number_format.md) keeps the exact spelling
|
||||
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
||||
[`BasicJsonType::dump()`](../../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
||||
new value. See [Writing a view back](reading.md#writing-a-view-back).
|
||||
|
||||
??? example "Example: patch a configuration, keeping member order and number spellings"
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_editable_document;
|
||||
using nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "price": 19.90})";
|
||||
std::cout << text << "\n\n";
|
||||
|
||||
// patch two fields -- "price" is never touched
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
doc.set(doc.root(), "host", "db2");
|
||||
doc.set(doc.root(), "retries", 3);
|
||||
|
||||
// member order (the new member at the end) and the untouched number's
|
||||
// exact spelling survive
|
||||
std::cout << doc.root().dump(-1, ' ', false, json_editable_view::number_format::source) << '\n';
|
||||
|
||||
// the same patch on a plain json value: keys are sorted (object_t is a
|
||||
// std::map), and "price" is rewritten even though the patch never
|
||||
// touched it
|
||||
json plain = json::parse(text);
|
||||
plain["host"] = "db2";
|
||||
plain["retries"] = 3;
|
||||
std::cout << plain.dump() << '\n';
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}
|
||||
|
||||
{"name":"cache","host":"db2","port":6379,"price":19.90,"retries":3}
|
||||
{"host":"db2","name":"cache","port":6379,"price":19.9,"retries":3}
|
||||
|
||||
```
|
||||
|
||||
## What stays valid, and what an edit costs
|
||||
|
||||
The source text itself is **never written**, and the parsed index never moves — a value keeps the node it was
|
||||
parsed into for as long as it is not itself replaced. So every [view](../../api/basic_json_view/index.md) taken before
|
||||
an edit, including a previously obtained [`root()`](../../api/basic_json_document/root.md), stays valid and, if it
|
||||
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
||||
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
||||
invalidate is the **iterators** taken over an edited array or object: the first time an element is added to it,
|
||||
inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move
|
||||
from the parsed, fixed layout to a growable block of links so that
|
||||
[`push_back`](../../api/basic_json_document/push_back.md) can later grow it in amortized constant time — existing
|
||||
elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with
|
||||
[`get_string()`](../../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
||||
edits. See [`basic_json_document`'s Edits](../../api/basic_json_document/index.md#edits) for the details, and
|
||||
[`set`'s Exception safety](../../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
||||
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
||||
the index is described in the [architecture overview](../../home/architecture.md#node-index-of-json-views).
|
||||
|
||||
Step through the edits of a small document (the node fields follow the
|
||||
[architecture overview](../../home/architecture.md#node-index-of-json-views)):
|
||||
|
||||
<div class="nj-jsonview-steps" data-nj-jsonview-steps>
|
||||
<figure class="nj-jsonview-step" data-label="After parsing">
|
||||
<div class="nj-jsonview-scroll">
|
||||
<svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewBox="0 0 840 372" role="img" aria-labelledby="fig-jve0-title fig-jve0-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||
<title id="fig-jve0-title">Step 1 of 5: parsed</title>
|
||||
<desc id="fig-jve0-title-d">After parse: nine nodes, nothing owned yet. The dump with number_format::source is {"port":6379,"price":19.90,"tags":["eu","a\"b"]}.</desc>
|
||||
<defs>
|
||||
<marker id="jve0-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve0-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve0-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve0-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
</defs>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong" x="16" y="22" xml:space="preserve">parsed index: fixed, never moves</text>
|
||||
<text class="nj-jsonview-note" x="824" y="22" text-anchor="end" xml:space="preserve">the source text is never written</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="36" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="42" y="47" xml:space="preserve">0</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="78" y="61" text-anchor="middle" xml:space="preserve">object</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="78" y="77" text-anchor="middle" xml:space="preserve">{ } 3</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="124" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="130" y="47" xml:space="preserve">1</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="166" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="166" y="77" text-anchor="middle" xml:space="preserve">port</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="212" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="218" y="47" xml:space="preserve">2</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="254" y="61" text-anchor="middle" xml:space="preserve">uint</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="254" y="77" text-anchor="middle" xml:space="preserve">6379</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="300" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="306" y="47" xml:space="preserve">3</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="342" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="342" y="77" text-anchor="middle" xml:space="preserve">price</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="388" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="394" y="47" xml:space="preserve">4</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="430" y="61" text-anchor="middle" xml:space="preserve">float</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="430" y="77" text-anchor="middle" xml:space="preserve">19.90</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="476" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="482" y="47" xml:space="preserve">5</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="518" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="518" y="77" text-anchor="middle" xml:space="preserve">tags</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="564" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="570" y="47" xml:space="preserve">6</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="606" y="61" text-anchor="middle" xml:space="preserve">array</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="606" y="77" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-str" x="652" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="658" y="47" xml:space="preserve">7</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="694" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="694" y="77" text-anchor="middle" xml:space="preserve">"eu"</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="746" y="47" xml:space="preserve">8</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-dec" x="782" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="782" y="77" text-anchor="middle" xml:space="preserve">a"b</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong nj-jsonview-c-own" x="16" y="288" xml:space="preserve">owned storage: allocated on demand, never moves</text>
|
||||
<rect class="nj-jsonview-store" x="10" y="192" width="820" height="54" rx="8"/>
|
||||
<text class="nj-jsonview-note" x="26" y="224" xml:space="preserve">empty: a read-only json_document has none of this</text>
|
||||
<path class="nj-jsonview-sep" d="M16 300 H824"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="318" xml:space="preserve">view v = root()["tags"] (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="318" xml:space="preserve">valid, sees every edit</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="338" xml:space="preserve">iterator over tags (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="338" xml:space="preserve">valid</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs nj-jsonview-muted" x="16" y="360" xml:space="preserve">dump(): {"port":6379,"price":19.90,"tags":["eu","a\"b"]}</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<figcaption>Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="set an existing member">
|
||||
<div class="nj-jsonview-scroll">
|
||||
<svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewBox="0 0 840 372" role="img" aria-labelledby="fig-jve1-title fig-jve1-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||
<title id="fig-jve1-title">Step 2 of 5: doc.set(doc.root(), "port", 6380)</title>
|
||||
<desc id="fig-jve1-title-d">set: the node of the integer is overwritten in place. The dump with number_format::source is {"port":6380,"price":19.90,"tags":["eu","a\"b"]}.</desc>
|
||||
<defs>
|
||||
<marker id="jve1-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve1-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve1-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve1-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
</defs>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong" x="16" y="22" xml:space="preserve">parsed index: fixed, never moves</text>
|
||||
<text class="nj-jsonview-note" x="824" y="22" text-anchor="end" xml:space="preserve">the source text is never written</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="36" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="42" y="47" xml:space="preserve">0</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="78" y="61" text-anchor="middle" xml:space="preserve">object</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="78" y="77" text-anchor="middle" xml:space="preserve">{ } 3</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="124" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="130" y="47" xml:space="preserve">1</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="166" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="166" y="77" text-anchor="middle" xml:space="preserve">port</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-num" x="212" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="218" y="47" xml:space="preserve">2</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="254" y="61" text-anchor="middle" xml:space="preserve">uint</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="254" y="77" text-anchor="middle" xml:space="preserve">6380</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="300" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="306" y="47" xml:space="preserve">3</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="342" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="342" y="77" text-anchor="middle" xml:space="preserve">price</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="388" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="394" y="47" xml:space="preserve">4</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="430" y="61" text-anchor="middle" xml:space="preserve">float</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="430" y="77" text-anchor="middle" xml:space="preserve">19.90</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="476" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="482" y="47" xml:space="preserve">5</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="518" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="518" y="77" text-anchor="middle" xml:space="preserve">tags</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="564" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="570" y="47" xml:space="preserve">6</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="606" y="61" text-anchor="middle" xml:space="preserve">array</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="606" y="77" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-str" x="652" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="658" y="47" xml:space="preserve">7</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="694" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="694" y="77" text-anchor="middle" xml:space="preserve">"eu"</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="746" y="47" xml:space="preserve">8</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-dec" x="782" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="782" y="77" text-anchor="middle" xml:space="preserve">a"b</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong nj-jsonview-c-own" x="16" y="288" xml:space="preserve">owned storage: allocated on demand, never moves</text>
|
||||
<rect class="nj-jsonview-store" x="10" y="192" width="820" height="54" rx="8"/>
|
||||
<text class="nj-jsonview-note" x="26" y="224" xml:space="preserve">empty: a read-only json_document has none of this</text>
|
||||
<path class="nj-jsonview-sep" d="M16 300 H824"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="318" xml:space="preserve">view v = root()["tags"] (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="318" xml:space="preserve">valid, sees every edit</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="338" xml:space="preserve">iterator over tags (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="338" xml:space="preserve">valid</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs nj-jsonview-muted" x="16" y="360" xml:space="preserve">dump(): {"port":6380,"price":19.90,"tags":["eu","a\"b"]}</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<figcaption>Step 2 of 5. <code>doc.set(doc.root(), "port", 6380)</code> overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="erase an array element">
|
||||
<div class="nj-jsonview-scroll">
|
||||
<svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewBox="0 0 840 372" role="img" aria-labelledby="fig-jve2-title fig-jve2-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||
<title id="fig-jve2-title">Step 3 of 5: doc.erase(doc.root()["tags"], 0)</title>
|
||||
<desc id="fig-jve2-title-d">erase: the array moves to a block of links in owned storage. The dump with number_format::source is {"port":6380,"price":19.90,"tags":["a\"b"]}.</desc>
|
||||
<defs>
|
||||
<marker id="jve2-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve2-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve2-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve2-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
</defs>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong" x="16" y="22" xml:space="preserve">parsed index: fixed, never moves</text>
|
||||
<text class="nj-jsonview-note" x="824" y="22" text-anchor="end" xml:space="preserve">the source text is never written</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="36" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="42" y="47" xml:space="preserve">0</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="78" y="61" text-anchor="middle" xml:space="preserve">object</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="78" y="77" text-anchor="middle" xml:space="preserve">{ } 3</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="124" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="130" y="47" xml:space="preserve">1</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="166" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="166" y="77" text-anchor="middle" xml:space="preserve">port</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="212" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="218" y="47" xml:space="preserve">2</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="254" y="61" text-anchor="middle" xml:space="preserve">uint</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="254" y="77" text-anchor="middle" xml:space="preserve">6380</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="300" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="306" y="47" xml:space="preserve">3</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="342" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="342" y="77" text-anchor="middle" xml:space="preserve">price</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="388" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="394" y="47" xml:space="preserve">4</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="430" y="61" text-anchor="middle" xml:space="preserve">float</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="430" y="77" text-anchor="middle" xml:space="preserve">19.90</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="476" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="482" y="47" xml:space="preserve">5</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="518" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="518" y="77" text-anchor="middle" xml:space="preserve">tags</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="564" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="570" y="47" xml:space="preserve">6</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="606" y="61" text-anchor="middle" xml:space="preserve">array</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-struct nj-jsonview-strong" x="643" y="47" text-anchor="end" xml:space="preserve">moved</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="606" y="77" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-gap nj-jsonview-c-str" x="652" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="658" y="47" xml:space="preserve">7</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="694" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="694" y="77" text-anchor="middle" xml:space="preserve">"eu"</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="694" y="98" text-anchor="middle" xml:space="preserve">unreferenced</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="746" y="47" xml:space="preserve">8</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-dec" x="782" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="782" y="77" text-anchor="middle" xml:space="preserve">a"b</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong nj-jsonview-c-own" x="16" y="288" xml:space="preserve">owned storage: allocated on demand, never moves</text>
|
||||
<rect class="nj-jsonview-store" x="10" y="192" width="820" height="54" rx="8"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-struct" x="16" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="66" y="207" text-anchor="end" xml:space="preserve">0'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="43" y="220" text-anchor="middle" xml:space="preserve">header</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="43" y="235" text-anchor="middle" xml:space="preserve">[ ] 1</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-own" x="73" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="123" y="207" text-anchor="end" xml:space="preserve">1'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="100" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="100" y="235" text-anchor="middle" xml:space="preserve">→ 8</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M100 196 C100 140 782 140 782 84" marker-end="url(#jve2-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-free nj-jsonview-c-own" x="130" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="157" y="224" text-anchor="middle" xml:space="preserve">free</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-struct nj-jsonview-dash" d="M598 84 C598 140 43 140 43 196" marker-end="url(#jve2-struct)"/>
|
||||
<path class="nj-jsonview-sep" d="M16 300 H824"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="318" xml:space="preserve">view v = root()["tags"] (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="318" xml:space="preserve">valid, sees every edit</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="338" xml:space="preserve">iterator over tags (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-bad nj-jsonview-strong" x="320" y="338" xml:space="preserve">invalid: tags no longer uses the parsed layout</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs nj-jsonview-muted" x="16" y="360" xml:space="preserve">dump(): {"port":6380,"price":19.90,"tags":["a\"b"]}</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<figcaption>Step 3 of 5. <code>doc.erase(doc.root()["tags"], 0)</code>: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of <code>"eu"</code> stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="push_back onto the array">
|
||||
<div class="nj-jsonview-scroll">
|
||||
<svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewBox="0 0 840 372" role="img" aria-labelledby="fig-jve3-title fig-jve3-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||
<title id="fig-jve3-title">Step 4 of 5: doc.push_back(doc.root()["tags"], "new")</title>
|
||||
<desc id="fig-jve3-title-d">push_back: a new node, and a link appended to the block. The dump with number_format::source is {"port":6380,"price":19.90,"tags":["a\"b","new"]}.</desc>
|
||||
<defs>
|
||||
<marker id="jve3-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve3-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve3-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve3-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
</defs>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong" x="16" y="22" xml:space="preserve">parsed index: fixed, never moves</text>
|
||||
<text class="nj-jsonview-note" x="824" y="22" text-anchor="end" xml:space="preserve">the source text is never written</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="36" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="42" y="47" xml:space="preserve">0</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="78" y="61" text-anchor="middle" xml:space="preserve">object</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="78" y="77" text-anchor="middle" xml:space="preserve">{ } 3</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="124" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="130" y="47" xml:space="preserve">1</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="166" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="166" y="77" text-anchor="middle" xml:space="preserve">port</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="212" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="218" y="47" xml:space="preserve">2</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="254" y="61" text-anchor="middle" xml:space="preserve">uint</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="254" y="77" text-anchor="middle" xml:space="preserve">6380</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="300" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="306" y="47" xml:space="preserve">3</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="342" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="342" y="77" text-anchor="middle" xml:space="preserve">price</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="388" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="394" y="47" xml:space="preserve">4</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="430" y="61" text-anchor="middle" xml:space="preserve">float</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="430" y="77" text-anchor="middle" xml:space="preserve">19.90</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="476" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="482" y="47" xml:space="preserve">5</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="518" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="518" y="77" text-anchor="middle" xml:space="preserve">tags</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="564" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="570" y="47" xml:space="preserve">6</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="606" y="61" text-anchor="middle" xml:space="preserve">array</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-struct nj-jsonview-strong" x="643" y="47" text-anchor="end" xml:space="preserve">moved</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="606" y="77" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-gap nj-jsonview-c-str" x="652" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="658" y="47" xml:space="preserve">7</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="694" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="694" y="77" text-anchor="middle" xml:space="preserve">"eu"</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="694" y="98" text-anchor="middle" xml:space="preserve">unreferenced</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="746" y="47" xml:space="preserve">8</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-dec" x="782" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="782" y="77" text-anchor="middle" xml:space="preserve">a"b</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong nj-jsonview-c-own" x="16" y="288" xml:space="preserve">owned storage: allocated on demand, never moves</text>
|
||||
<rect class="nj-jsonview-store" x="10" y="192" width="820" height="54" rx="8"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="16" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="66" y="207" text-anchor="end" xml:space="preserve">0'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="43" y="220" text-anchor="middle" xml:space="preserve">header</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="43" y="235" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-own" x="73" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="123" y="207" text-anchor="end" xml:space="preserve">1'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="100" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="100" y="235" text-anchor="middle" xml:space="preserve">→ 8</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M100 196 C100 140 782 140 782 84" marker-end="url(#jve3-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-own" x="130" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="180" y="207" text-anchor="end" xml:space="preserve">2'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="157" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="157" y="235" text-anchor="middle" xml:space="preserve">→ 3'</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M157 242 C157 268 214 268 214 244" marker-end="url(#jve3-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-str" x="187" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="237" y="207" text-anchor="end" xml:space="preserve">3'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="214" y="220" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="214" y="235" text-anchor="middle" xml:space="preserve">"new"</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-struct nj-jsonview-dash" d="M598 84 C598 140 43 140 43 196" marker-end="url(#jve3-struct)"/>
|
||||
<text class="nj-jsonview-note" x="588" y="282" text-anchor="end" xml:space="preserve">edited text</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-str" x="596" y="264" width="42" height="26" rx="4"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="602" y="282" xml:space="preserve">new</text>
|
||||
<path class="nj-jsonview-sep" d="M16 300 H824"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="318" xml:space="preserve">view v = root()["tags"] (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="318" xml:space="preserve">valid, sees every edit</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="338" xml:space="preserve">iterator over tags (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-bad nj-jsonview-strong" x="320" y="338" xml:space="preserve">invalid: tags no longer uses the parsed layout</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs nj-jsonview-muted" x="16" y="360" xml:space="preserve">dump(): {"port":6380,"price":19.90,"tags":["a\"b","new"]}</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<figcaption>Step 4 of 5. <code>doc.push_back(doc.root()["tags"], "new")</code>: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.</figcaption>
|
||||
</figure>
|
||||
<figure class="nj-jsonview-step" data-label="set a new member">
|
||||
<div class="nj-jsonview-scroll">
|
||||
<svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewBox="0 0 840 372" role="img" aria-labelledby="fig-jve4-title fig-jve4-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||
<title id="fig-jve4-title">Step 5 of 5: doc.set(doc.root(), "retries", 3)</title>
|
||||
<desc id="fig-jve4-title-d">set of a new member: the object moves to a block as well. The dump with number_format::source is {"port":6380,"price":19.90,"tags":["a\"b","new"],"retries":3}.</desc>
|
||||
<defs>
|
||||
<marker id="jve4-line" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve4-own" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve4-struct" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
<marker id="jve4-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
</defs>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong" x="16" y="22" xml:space="preserve">parsed index: fixed, never moves</text>
|
||||
<text class="nj-jsonview-note" x="824" y="22" text-anchor="end" xml:space="preserve">the source text is never written</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="36" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="42" y="47" xml:space="preserve">0</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="78" y="61" text-anchor="middle" xml:space="preserve">object</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-struct nj-jsonview-strong" x="115" y="47" text-anchor="end" xml:space="preserve">moved</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="78" y="77" text-anchor="middle" xml:space="preserve">{ } 3</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="124" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="130" y="47" xml:space="preserve">1</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="166" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="166" y="77" text-anchor="middle" xml:space="preserve">port</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="212" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="218" y="47" xml:space="preserve">2</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="254" y="61" text-anchor="middle" xml:space="preserve">uint</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="254" y="77" text-anchor="middle" xml:space="preserve">6380</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="300" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="306" y="47" xml:space="preserve">3</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="342" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="342" y="77" text-anchor="middle" xml:space="preserve">price</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-num" x="388" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="394" y="47" xml:space="preserve">4</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="430" y="61" text-anchor="middle" xml:space="preserve">float</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="430" y="77" text-anchor="middle" xml:space="preserve">19.90</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-key" x="476" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="482" y="47" xml:space="preserve">5</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="518" y="61" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="518" y="77" text-anchor="middle" xml:space="preserve">tags</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="564" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="570" y="47" xml:space="preserve">6</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="606" y="61" text-anchor="middle" xml:space="preserve">array</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-struct nj-jsonview-strong" x="643" y="47" text-anchor="end" xml:space="preserve">moved</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="606" y="77" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-gap nj-jsonview-c-str" x="652" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="658" y="47" xml:space="preserve">7</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="694" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="694" y="77" text-anchor="middle" xml:space="preserve">"eu"</text>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="694" y="98" text-anchor="middle" xml:space="preserve">unreferenced</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-dec" x="740" y="34" width="84" height="50" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="746" y="47" xml:space="preserve">8</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-dec" x="782" y="61" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="782" y="77" text-anchor="middle" xml:space="preserve">a"b</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-strong nj-jsonview-c-own" x="16" y="288" xml:space="preserve">owned storage: allocated on demand, never moves</text>
|
||||
<rect class="nj-jsonview-store" x="10" y="192" width="820" height="54" rx="8"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-struct" x="16" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="66" y="207" text-anchor="end" xml:space="preserve">0'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="43" y="220" text-anchor="middle" xml:space="preserve">header</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="43" y="235" text-anchor="middle" xml:space="preserve">[ ] 2</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-own" x="73" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="123" y="207" text-anchor="end" xml:space="preserve">1'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="100" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="100" y="235" text-anchor="middle" xml:space="preserve">→ 8</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M100 196 C100 140 782 140 782 84" marker-end="url(#jve4-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-own" x="130" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="180" y="207" text-anchor="end" xml:space="preserve">2'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="157" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="157" y="235" text-anchor="middle" xml:space="preserve">→ 3'</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M157 242 C157 268 214 268 214 244" marker-end="url(#jve4-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-str" x="187" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="237" y="207" text-anchor="end" xml:space="preserve">3'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-str" x="214" y="220" text-anchor="middle" xml:space="preserve">string</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="214" y="235" text-anchor="middle" xml:space="preserve">"new"</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-num" x="244" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="294" y="207" text-anchor="end" xml:space="preserve">4'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x="271" y="220" text-anchor="middle" xml:space="preserve">uint</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="271" y="235" text-anchor="middle" xml:space="preserve">3</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-struct" x="301" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="351" y="207" text-anchor="end" xml:space="preserve">5'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x="328" y="220" text-anchor="middle" xml:space="preserve">header</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="328" y="235" text-anchor="middle" xml:space="preserve">{ } 4</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-key" x="358" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="408" y="207" text-anchor="end" xml:space="preserve">6'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="385" y="220" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="385" y="235" text-anchor="middle" xml:space="preserve">port</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-own" x="415" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="465" y="207" text-anchor="end" xml:space="preserve">7'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="442" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="442" y="235" text-anchor="middle" xml:space="preserve">→ 2</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M442 196 C442 140 254 140 254 84" marker-end="url(#jve4-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-key" x="472" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="522" y="207" text-anchor="end" xml:space="preserve">8'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="499" y="220" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="499" y="235" text-anchor="middle" xml:space="preserve">price</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-own" x="529" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="579" y="207" text-anchor="end" xml:space="preserve">9'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="556" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="556" y="235" text-anchor="middle" xml:space="preserve">→ 4</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M556 196 C556 140 430 140 430 84" marker-end="url(#jve4-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-key" x="586" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="636" y="207" text-anchor="end" xml:space="preserve">10'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="613" y="220" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="613" y="235" text-anchor="middle" xml:space="preserve">tags</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-own" x="643" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="693" y="207" text-anchor="end" xml:space="preserve">11'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="670" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="670" y="235" text-anchor="middle" xml:space="preserve">→ 6</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M670 196 C670 140 614 140 614 84" marker-end="url(#jve4-own)"/>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-key" x="700" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="750" y="207" text-anchor="end" xml:space="preserve">12'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x="727" y="220" text-anchor="middle" xml:space="preserve">key</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="727" y="235" text-anchor="middle" xml:space="preserve">retries</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-hot nj-jsonview-c-own" x="757" y="196" width="54" height="46" rx="5"/>
|
||||
<text class="nj-jsonview-tiny" x="807" y="207" text-anchor="end" xml:space="preserve">13'</text>
|
||||
<text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-own" x="784" y="220" text-anchor="middle" xml:space="preserve">link</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="784" y="235" text-anchor="middle" xml:space="preserve">→ 4'</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-own" d="M784 242 C784 268 271 268 271 244" marker-end="url(#jve4-own)"/>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-struct nj-jsonview-dash" d="M598 84 C598 140 43 140 43 196" marker-end="url(#jve4-struct)"/>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-struct nj-jsonview-dash" d="M70 84 C70 140 328 140 328 196" marker-end="url(#jve4-struct)"/>
|
||||
<text class="nj-jsonview-note" x="588" y="282" text-anchor="end" xml:space="preserve">edited text</text>
|
||||
<rect class="nj-jsonview-node nj-jsonview-c-str" x="596" y="264" width="112" height="26" rx="4"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="602" y="282" xml:space="preserve">newretries</text>
|
||||
<path class="nj-jsonview-sep" d="M16 300 H824"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="318" xml:space="preserve">view v = root()["tags"] (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-struct nj-jsonview-strong" x="320" y="318" xml:space="preserve">valid, sees every edit</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs" x="16" y="338" xml:space="preserve">iterator over tags (taken after parse):</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-c-bad nj-jsonview-strong" x="320" y="338" xml:space="preserve">invalid: tags no longer uses the parsed layout</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-xs nj-jsonview-muted" x="16" y="360" xml:space="preserve">dump(): {"port":6380,"price":19.90,"tags":["a\"b","new"],"retries":3}</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<figcaption>Step 5 of 5. <code>doc.set(doc.root(), "retries", 3)</code>: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.</p>
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`json_editable_document`](../../api/json_editable_document.md) - and [`ordered_json_editable_document`](../../api/ordered_json_editable_document.md) - the documents that can be edited in place
|
||||
- [`basic_json_document`'s Edits](../../api/basic_json_document/index.md#edits) - the rules of edits in detail
|
||||
- [Differences from `basic_json`](differences.md#exception-context) - source positions after edits
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,47 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/editing/" rel="canonical"><link href=../generic_code/ rel=prev><link href=../images/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Editing a document - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#editing-a-document class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Editing a document </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d=Line truncated
|
||||
"name":"cache",
|
||||
"port":6379,
|
||||
"price":<span class=nj-jsonview-bad>19.9</span>,
|
||||
"retries":3}</pre></div> <div><p class=nj-jsonview-cmp-h><code>ordered_json</code></p><p class=nj-jsonview-cmp-v>key order kept, numbers rewritten</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class=nj-jsonview-bad>19.9</span>,
|
||||
"retries":3}</pre></div> <div class=nj-jsonview-cmp--best><p class=nj-jsonview-cmp-h><code>json_editable_document</code></p><p class=nj-jsonview-cmp-v>order and spelling kept</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class=nj-jsonview-ok>19.90</span>,
|
||||
"retries":3}</pre></div> </div> <p>An editable document keeps both. <a href=../../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document writes members in document order — a member <a href=../../../api/basic_json_document/set/ ><code>set</code></a> added goes at the end, exactly where it was inserted, and an <a href=../../../api/basic_json_document/erase/ ><code>erase</code></a>d member simply leaves a gap: everything around it keeps its place — and <a href=../../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> keeps the exact spelling of every number an edit did not itself touch; a number an edit <em>did</em> touch is written the way <a href=../../../api/basic_json/dump/ ><code>BasicJsonType::dump()</code></a> would write it, since there is no source spelling for a brand new value. See <a href=../reading/#writing-a-view-back>Writing a view back</a>.</p> <details class=example> <summary>Example: patch a configuration, keeping member order and number spellings</summary> <div class=highlight><pre><span></span><code><span class=cp>#include</span><span class=w> </span><span class=cpf><iostream></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=p>;</span>
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_editable_document</span><span class=p>;</span>
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_editable_view</span><span class=p>;</span>
|
||||
|
||||
<span class=kt>int</span><span class=w> </span><span class=nf>main</span><span class=p>()</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=c1>// a configuration file, as it might be read from disk</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=s>"</span><span class=se>\n\n</span><span class=s>"</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// patch two fields -- "price" is never touched</span>
|
||||
<span class=w> </span><span class=n>json_editable_document</span><span class=w> </span><span class=n>doc</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_editable_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>set</span><span class=p>(</span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>(),</span><span class=w> </span><span class=s>"host"</span><span class=p>,</span><span class=w> </span><span class=s>"db2"</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>set</span><span class=p>(</span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>(),</span><span class=w> </span><span class=s>"retries"</span><span class=p>,</span><span class=w> </span><span class=mi>3</span><span class=p>);</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// member order (the new member at the end) and the untouched number's</span>
|
||||
<span class=w> </span><span class=c1>// exact spelling survive</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>dump</span><span class=p>(</span><span class=mi>-1</span><span class=p>,</span><span class=w> </span><span class=sc>' '</span><span class=p>,</span><span class=w> </span><span class=nb>false</span><span class=p>,</span><span class=w> </span><span class=n>json_editable_view</span><span class=o>::</span><span class=n>number_format</span><span class=o>::</span><span class=n>source</span><span class=p>)</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// the same patch on a plain json value: keys are sorted (object_t is a</span>
|
||||
<span class=w> </span><span class=c1>// std::map), and "price" is rewritten even though the patch never</span>
|
||||
<span class=w> </span><span class=c1>// touched it</span>
|
||||
<span class=w> </span><span class=n>json</span><span class=w> </span><span class=n>plain</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>plain</span><span class=p>[</span><span class=s>"host"</span><span class=p>]</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=s>"db2"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>plain</span><span class=p>[</span><span class=s>"retries"</span><span class=p>]</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>3</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>plain</span><span class=p>.</span><span class=n>dump</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=p>{</span><span class=nt>"name"</span><span class=p>:</span><span class=w> </span><span class=s2>"cache"</span><span class=p>,</span><span class=w> </span><span class=nt>"host"</span><span class=p>:</span><span class=w> </span><span class=s2>"db1"</span><span class=p>,</span><span class=w> </span><span class=nt>"port"</span><span class=p>:</span><span class=w> </span><span class=mi>6379</span><span class=p>,</span><span class=w> </span><span class=nt>"price"</span><span class=p>:</span><span class=w> </span><span class=mf>19.90</span><span class=p>}</span>
|
||||
|
||||
<span class=p>{</span><span class=nt>"name"</span><span class=p>:</span><span class=s2>"cache"</span><span class=p>,</span><span class=nt>"host"</span><span class=p>:</span><span class=s2>"db2"</span><span class=p>,</span><span class=nt>"port"</span><span class=p>:</span><span class=mi>6379</span><span class=p>,</span><span class=nt>"price"</span><span class=p>:</span><span class=mf>19.90</span><span class=p>,</span><span class=nt>"retries"</span><span class=p>:</span><span class=mi>3</span><span class=p>}</span>
|
||||
<span class=p>{</span><span class=nt>"host"</span><span class=p>:</span><span class=s2>"db2"</span><span class=p>,</span><span class=nt>"name"</span><span class=p>:</span><span class=s2>"cache"</span><span class=p>,</span><span class=nt>"port"</span><span class=p>:</span><span class=mi>6379</span><span class=p>,</span><span class=nt>"price"</span><span class=p>:</span><span class=mf>19.9</span><span class=p>,</span><span class=nt>"retries"</span><span class=p>:</span><span class=mi>3</span><span class=p>}</span>
|
||||
</code></pre></div> </details> <h2 id=what-stays-valid-and-what-an-edit-costs>What stays valid, and what an edit costs<a class=headerlink href=#what-stays-valid-and-what-an-edit-costs title="Permanent link">¶</a></h2> <p>The source text itself is <strong>never written</strong>, 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 <a href=../../../api/basic_json_view/ >view</a> taken before an edit, including a previously obtained <a href=../../../api/basic_json_document/root/ ><code>root()</code></a>, 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 <strong>iterators</strong> 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 <a href=../../../api/basic_json_document/push_back/ ><code>push_back</code></a> 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 <a href=../../../api/basic_json_view/get_string/ ><code>get_string()</code></a> is unaffected either way and stays valid across further edits. See <a href=../../../api/basic_json_document/#edits><code>basic_json_document</code>'s Edits</a> for the details, and <a href=../../../api/basic_json_document/set/#exception-safety><code>set</code>'s Exception safety</a> for what an edit guarantees if it throws (the <em>basic</em> guarantee, not the strong one <code>dump()</code> and the read-only functions provide). How edits are kept in the index is described in the <a href=../../../home/architecture/#node-index-of-json-views>architecture overview</a>.</p> <p>Step through the edits of a small document (the node fields follow the <a href=../../../home/architecture/#node-index-of-json-views>architecture overview</a>):</p> <div class=nj-jsonview-steps data-nj-jsonview-steps> <figure class=nj-jsonview-step data-label="After parsing"> <div class=nj-jsonview-scroll> <svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewbox="0 0 840 372" role=img aria-labelledby="fig-jve0-title fig-jve0-title-d" xmlns=http://www.w3.org/2000/svg> <title id=fig-jve0-title>Step 1 of 5: parsed</title> <desc id=fig-jve0-title-d>After parse: nine nodes, nothing owned yet. The dump with number_format::source is {"port":6379,"price":19.90,"tags":["eu","a\"b"]}.</desc> <defs> <marker id=jve0-line viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-own viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-struct viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-bad viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> </defs> <text class="nj-jsonview-note nj-jsonview-strong" x=16 y=22 xml:space=preserve>parsed index: fixed, never moves</text> <text class=nj-jsonview-note x=824 y=22 text-anchor=end xml:space=preserve>the source text is never written</text> <rect x=36 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-struct"/> <text class=nj-jsonview-tiny x=42 y=47 xml:space=preserve>0</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x=78 y=61 text-anchor=middle xml:space=preserve>object</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=78 y=77 text-anchor=middle xml:space=preserve>{ } 3</text> <rect x=124 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-key"/> <text class=nj-jsonview-tiny x=130 y=47 xml:space=preserve>1</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x=166 y=61 text-anchor=middle xml:space=preserve>key</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=166 y=77 text-anchor=middle xml:space=preserve>port</text> <rect x=212 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-num"/> <text class=nj-jsonview-tiny x=218 y=47 xml:space=preserve>2</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x=254 y=61 text-anchor=middle xml:space=preserve>uint</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=254 y=77 text-anchor=middle xml:space=preserve>Line truncated
|
||||
@@ -0,0 +1,125 @@
|
||||
# Editing a document
|
||||
|
||||
A [`json_document`](https://json.nlohmann.me/api/json_document/index.md)/[`json_view`](https://json.nlohmann.me/api/json_view/index.md) lets you look at a parsed text without copying it, but not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](https://json.nlohmann.me/api/basic_json_document/index.md) — more conveniently spelled [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) or [`ordered_json_editable_document`](https://json.nlohmann.me/api/ordered_json_editable_document/index.md) — also lets you [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) a value, [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md) onto or [`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md) into an array, and [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md) an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
|
||||
|
||||
The [mode](https://json.nlohmann.me/api/view_mode/index.md) defaults to `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 `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 `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 `19.90` to `19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced it to a `double`/`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()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document writes members in document order — a member [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) added goes at the end, exactly where it was inserted, and an [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md)d member simply leaves a gap: everything around it keeps its place — and [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.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()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would write it, since there is no source spelling for a brand new value. See [Writing a view back](https://json.nlohmann.me/features/json_view/reading/#writing-a-view-back).
|
||||
|
||||
Example: patch a configuration, keeping member order and number spellings
|
||||
|
||||
```
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_editable_document;
|
||||
using nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "price": 19.90})";
|
||||
std::cout << text << "\n\n";
|
||||
|
||||
// patch two fields -- "price" is never touched
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
doc.set(doc.root(), "host", "db2");
|
||||
doc.set(doc.root(), "retries", 3);
|
||||
|
||||
// member order (the new member at the end) and the untouched number's
|
||||
// exact spelling survive
|
||||
std::cout << doc.root().dump(-1, ' ', false, json_editable_view::number_format::source) << '\n';
|
||||
|
||||
// the same patch on a plain json value: keys are sorted (object_t is a
|
||||
// std::map), and "price" is rewritten even though the patch never
|
||||
// touched it
|
||||
json plain = json::parse(text);
|
||||
plain["host"] = "db2";
|
||||
plain["retries"] = 3;
|
||||
std::cout << plain.dump() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}
|
||||
|
||||
{"name":"cache","host":"db2","port":6379,"price":19.90,"retries":3}
|
||||
{"host":"db2","name":"cache","port":6379,"price":19.9,"retries":3}
|
||||
```
|
||||
|
||||
## 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](https://json.nlohmann.me/api/basic_json_view/index.md) taken before an edit, including a previously obtained [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.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`](https://json.nlohmann.me/api/basic_json_document/push_back/index.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()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) is unaffected either way and stays valid across further edits. See [`basic_json_document`'s Edits](https://json.nlohmann.me/api/basic_json_document/#edits) for the details, and [`set`'s Exception safety](https://json.nlohmann.me/api/basic_json_document/set/#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](https://json.nlohmann.me/home/architecture/#node-index-of-json-views).
|
||||
|
||||
Step through the edits of a small document (the node fields follow the [architecture overview](https://json.nlohmann.me/home/architecture/#node-index-of-json-views)):
|
||||
|
||||
Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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](https://json.nlohmann.me/features/json_view/index.md) - overview
|
||||
- [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) - and [`ordered_json_editable_document`](https://json.nlohmann.me/api/ordered_json_editable_document/index.md) - the documents that can be edited in place
|
||||
- [`basic_json_document`'s Edits](https://json.nlohmann.me/api/basic_json_document/#edits) - the rules of edits in detail
|
||||
- [Differences from `basic_json`](https://json.nlohmann.me/features/json_view/differences/#exception-context) - source positions after edits
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0 unreleased.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Writing generic code
|
||||
|
||||
## View types
|
||||
|
||||
A view's type names its `BasicJsonType` and its [`view_mode`](../../api/view_mode.md):
|
||||
[`json_view`](../../api/json_view.md) is `#!cpp basic_json_view<json, view_mode::read_only>`, and
|
||||
[`json_editable_view`](../../api/json_editable_view.md) is `#!cpp basic_json_view<json, view_mode::editable>`. These
|
||||
are different types. The only conversion between them turns a view of a read-only document into a view of an
|
||||
editable document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
|
||||
|
||||
```cpp
|
||||
void print_names(json_view v); // accepts neither an ordered_json_view nor a json_editable_view
|
||||
void print_names(json_editable_view v); // also accepts a json_view, but no ordered_json_view
|
||||
```
|
||||
|
||||
This is the same pitfall as a `to_json`/`from_json` written for `json` instead of any `BasicJsonType`, which then does
|
||||
not work for `ordered_json`. The shortcuts are for declaring documents and views; a function that takes a view names
|
||||
the views it accepts with a template:
|
||||
|
||||
```cpp
|
||||
// views of editable documents only
|
||||
template<typename BasicJsonType>
|
||||
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
|
||||
|
||||
// views of read-only documents only
|
||||
template<typename BasicJsonType>
|
||||
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
|
||||
|
||||
// views of either document type
|
||||
template<typename BasicJsonType, view_mode Mode>
|
||||
void f(basic_json_view<BasicJsonType, Mode> v);
|
||||
```
|
||||
|
||||
Views are passed by value: they are two pointers, cheap to copy, like a `#!cpp std::string_view`. A `const&` works as
|
||||
well, but a non-`const` reference does not bind to the views that
|
||||
[`root()`](../../api/basic_json_document/root.md), [`operator[]`](../../api/basic_json_view/operator%5B%5D.md), and
|
||||
[`at()`](../../api/basic_json_view/at.md) return, so `#!cpp f(doc.root())` would not compile.
|
||||
|
||||
There is no conversion from an editable view to a read-only one, by design: the mode decides how a view walks the
|
||||
index. A read-only view reads a container in place, while a view of an editable document follows the links that edits
|
||||
leave behind, so a read-only view of an edited document would read stale values. The other direction is safe: a
|
||||
read-only document has no edits, so a view of it [converts](../../api/basic_json_view/basic_json_view.md) to an
|
||||
editable view, which reads it like an editable document before its first edit. A template deduces the mode without
|
||||
conversions, so the template forms above still accept only the modes they name. Views of both modes compare with
|
||||
[`operator==`](../../api/basic_json_view/operator_eq.md).
|
||||
|
||||
[`is_json_view<T>`](../../api/is_json_view.md) tells views from other types, for constraints that cannot be written as
|
||||
a `basic_json_view<...>` parameter.
|
||||
|
||||
## A `from_json()` that reads views
|
||||
|
||||
[`get<T>()`](../../api/basic_json_view/get.md) on a view calls `T`'s `from_json()` with the view itself if that
|
||||
`from_json()` accepts views, and with [`materialize()`](../../api/basic_json_view/materialize.md) otherwise. The
|
||||
`from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](../../api/macros/nlohmann_define_type_intrusive.md),
|
||||
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../../api/macros/nlohmann_define_derived_type.md), and
|
||||
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../../api/macros/nlohmann_json_serialize_enum.md) macros, and those of the library
|
||||
for the standard types, accept views, so these types need nothing else. For decoding the statuses of
|
||||
twitter.json into structs, reading the view is about 5 times faster than `#!cpp materialize().get<T>()`.
|
||||
|
||||
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
|
||||
|
||||
```cpp
|
||||
template < typename J, typename std::enable_if <
|
||||
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
|
||||
void from_json(const J& j, person& p)
|
||||
{
|
||||
p.name = j.at("name").template get<std::string>();
|
||||
p.age = j.at("age").template get<int>();
|
||||
}
|
||||
```
|
||||
|
||||
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`,
|
||||
`contains()`, `value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained
|
||||
`#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in
|
||||
[arbitrary type conversions](../arbitrary_types.md), is never called with a view, as it might use `basic_json`
|
||||
functions a view does not have; such a type is converted through `materialize()`. A `from_json()` for `json` only is
|
||||
converted through `materialize()` as well.
|
||||
|
||||
Reading the view and `#!cpp materialize().get<T>()` give the same result, except for an object with a repeated key;
|
||||
see [Duplicate keys](differences.md#duplicate-keys).
|
||||
|
||||
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](index.md) - overview
|
||||
- [`is_json_view`](../../api/is_json_view.md) - type trait to detect views
|
||||
- [`view_mode`](../../api/view_mode.md) - read-only and editable views
|
||||
- [Reading values](reading.md) - what `get<T>()` converts without materializing
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,21 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/generic_code/" rel="canonical"><link href=../reading/ rel=prev><link href=../editing/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Writing generic code - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#writing-generic-code class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Writing generic code </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><Line truncated
|
||||
<span class=kt>void</span><span class=w> </span><span class=nf>print_names</span><span class=p>(</span><span class=n>json_editable_view</span><span class=w> </span><span class=n>v</span><span class=p>);</span><span class=w> </span><span class=c1>// also accepts a json_view, but no ordered_json_view</span>
|
||||
</code></pre></div> <p>This is the same pitfall as a <code>to_json</code>/<code>from_json</code> written for <code>json</code> instead of any <code>BasicJsonType</code>, which then does not work for <code>ordered_json</code>. The shortcuts are for declaring documents and views; a function that takes a view names the views it accepts with a template:</p> <div class=highlight><pre><span></span><code><span class=c1>// views of editable documents only</span>
|
||||
<span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>f</span><span class=p>(</span><span class=n>basic_json_view</span><span class=o><</span><span class=n>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>editable</span><span class=o>></span><span class=w> </span><span class=n>v</span><span class=p>);</span>
|
||||
|
||||
<span class=c1>// views of read-only documents only</span>
|
||||
<span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>f</span><span class=p>(</span><span class=n>basic_json_view</span><span class=o><</span><span class=n>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>read_only</span><span class=o>></span><span class=w> </span><span class=n>v</span><span class=p>);</span>
|
||||
|
||||
<span class=c1>// views of either document type</span>
|
||||
<span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=w> </span><span class=n>Mode</span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>f</span><span class=p>(</span><span class=n>basic_json_view</span><span class=o><</span><span class=n>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>Mode</span><span class=o>></span><span class=w> </span><span class=n>v</span><span class=p>);</span>
|
||||
</code></pre></div> <p>Views are passed by value: they are two pointers, cheap to copy, like a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code>. A <code>const&</code> works as well, but a non-<code>const</code> reference does not bind to the views that <a href=../../../api/basic_json_document/root/ ><code>root()</code></a>, <a href=../../../api/basic_json_view/operator%5B%5D/ ><code>operator[]</code></a>, and <a href=../../../api/basic_json_view/at/ ><code>at()</code></a> return, so <code class=highlight><span class=n>f</span><span class=p>(</span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>())</span></code> would not compile.</p> <p>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 <a href=../../../api/basic_json_view/basic_json_view/ >converts</a> 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 <a href=../../../api/basic_json_view/operator_eq/ ><code>operator==</code></a>.</p> <p><a href=../../../api/is_json_view/ ><code>is_json_view<T></code></a> tells views from other types, for constraints that cannot be written as a <code>basic_json_view<...></code> parameter.</p> <h2 id=a-from_json-that-reads-views>A <code>from_json()</code> that reads views<a class=headerlink href=#a-from_json-that-reads-views title="Permanent link">¶</a></h2> <p><a href=../../../api/basic_json_view/get/ ><code>get<T>()</code></a> on a view calls <code>T</code>'s <code>from_json()</code> with the view itself if that <code>from_json()</code> accepts views, and with <a href=../../../api/basic_json_view/materialize/ ><code>materialize()</code></a> otherwise. The <code>from_json()</code> functions of the <a href=../../../api/macros/nlohmann_define_type_intrusive/ ><code>NLOHMANN_DEFINE_TYPE_*</code></a>, <a href=../../../api/macros/nlohmann_define_derived_type/ ><code>NLOHMANN_DEFINE_DERIVED_TYPE_*</code></a>, and <a href=../../../api/macros/nlohmann_json_serialize_enum/ ><code>NLOHMANN_JSON_SERIALIZE_ENUM</code></a> 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 <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>get</span><span class=o><</span><span class=n>T</span><span class=o>></span><span class=p>()</span></code>.</p> <p>A hand-written <code>from_json()</code> reads views if it is constrained to <code>json</code> values and views:</p> <div class=highlight><pre><span></span><code><span class=k>template</span><span class=w> </span><span class=o><</span><span class=w> </span><span class=k>typename</span><span class=w> </span><span class=nc>J</span><span class=p>,</span><span class=w> </span><span class=k>typename</span><span class=w> </span><span class=nc>std</span><span class=o>::</span><span class=n>enable_if</span><span class=w> </span><span class=o><</span>
|
||||
<span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>is_json_view</span><span class=o><</span><span class=n>J</span><span class=o>>::</span><span class=n>value</span><span class=w> </span><span class=o>||</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>is_same</span><span class=o><</span><span class=n>J</span><span class=p>,</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=o>>::</span><span class=n>value</span><span class=p>,</span><span class=w> </span><span class=kt>int</span><span class=w> </span><span class=o>>::</span><span class=n>type</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>0</span><span class=w> </span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>from_json</span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>J</span><span class=o>&</span><span class=w> </span><span class=n>j</span><span class=p>,</span><span class=w> </span><span class=n>person</span><span class=o>&</span><span class=w> </span><span class=n>p</span><span class=p>)</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>p</span><span class=p>.</span><span class=n>name</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>j</span><span class=p>.</span><span class=n>at</span><span class=p>(</span><span class=s>"name"</span><span class=p>).</span><span class=k>template</span><span class=w> </span><span class=n>get</span><span class=o><</span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=o>></span><span class=p>();</span>
|
||||
<span class=w> </span><span class=n>p</span><span class=p>.</span><span class=n>age</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>j</span><span class=p>.</span><span class=n>at</span><span class=p>(</span><span class=s>"age"</span><span class=p>).</span><span class=k>template</span><span class=w> </span><span class=n>get</span><span class=o><</span><span class=kt>int</span><span class=o>></span><span class=p>();</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Its body may only use what views and <code>basic_json</code> have in common: the type checks, <code>at()</code>, <code>operator[]</code>, <code>contains()</code>, <code>value()</code>, <code>size()</code>, iteration and <code>items()</code>, and <code>get<T>()</code>/<code>get_to()</code>. An unconstrained <code class=highlight><span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>></span><span class=w> </span><span class=kt>void</span><span class=w> </span><span class=n>from_json</span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>BasicJsonType</span><span class=o>&</span><span class=w> </span><span class=n>j</span><span class=p>,</span><span class=w> </span><span class=n>person</span><span class=o>&</span><span class=w> </span><span class=n>p</span><span class=p>)</span></code>, as in <a href=../../arbitrary_types/ >arbitrary type conversions</a>, is never called with a view, as it might use <code>basic_json</code> functions a view does not have; such a type is converted through <code>materialize()</code>. A <code>from_json()</code> for <code>json</code> only is converted through <code>materialize()</code> as well.</p> <p>Reading the view and <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>get</span><span class=o><</span><span class=n>T</span><span class=o>></span><span class=p>()</span></code> give the same result, except for an object with a repeated key; see <a href=../differences/#duplicate-keys>Duplicate keys</a>.</p> <p><code>to_json()</code> is unaffected: it writes <code>basic_json</code> values, never views.</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li><a href=../ >Zero-copy <abbr title="JavaScript Object Notation">JSON</abbr> views</a> - overview</li> <li><a href=../../../api/is_json_view/ ><code>is_json_view</code></a> - type trait to detect views</li> <li><a href=../../../api/view_mode/ ><code>view_mode</code></a> - read-only and editable views</li> <li><a href=../reading/ >Reading values</a> - what <code>get<T>()</code> converts without materializing</li> </ul> <h2 id=version-history>Version history<a class=headerlink href=#version-history title="Permanent link">¶</a></h2> <ul> <li>Added in version 3.13.0 <span class=unreleased-version title="Not part of a release yet; the latest release is 3.12.0.">unreleased</span>.</li> </ul> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date" title="November 9, 2026 07:12:00 UTC">November 9, 2026</span> </span> </aside> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <nav class="md-footer__inner md-grid" aria-label=Footer> <a href=../reading/ class="md-footer__link md-footer__link--prev" aria-label="Previous: Reading values"> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </div> <div class=md-footer__title> <span class=md-footer__direction> Previous </span> <div class=md-ellipsis> Reading values </div> </div> </a> <a href=../editing/ class="md-footer__link md-footer__link--next" aria-label="Next: Editing a document"> <div class=md-footer__title> <span class=md-footer__direction> Next </span> <div class=md-ellipsis> Editing a document </div> </div> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1Line truncated
|
||||
@@ -0,0 +1,65 @@
|
||||
# Writing generic code
|
||||
|
||||
## View types
|
||||
|
||||
A view's type names its `BasicJsonType` and its [`view_mode`](https://json.nlohmann.me/api/view_mode/index.md): [`json_view`](https://json.nlohmann.me/api/json_view/index.md) is `basic_json_view<json, view_mode::read_only>`, and [`json_editable_view`](https://json.nlohmann.me/api/json_editable_view/index.md) is `basic_json_view<json, view_mode::editable>`. These are different types. The only conversion between them turns a view of a read-only document into a view of an editable document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
// views of editable documents only
|
||||
template<typename BasicJsonType>
|
||||
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
|
||||
|
||||
// views of read-only documents only
|
||||
template<typename BasicJsonType>
|
||||
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
|
||||
|
||||
// views of either document type
|
||||
template<typename BasicJsonType, view_mode Mode>
|
||||
void f(basic_json_view<BasicJsonType, Mode> v);
|
||||
```
|
||||
|
||||
Views are passed by value: they are two pointers, cheap to copy, like a `std::string_view`. A `const&` works as well, but a non-`const` reference does not bind to the views that [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md), [`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md), and [`at()`](https://json.nlohmann.me/api/basic_json_view/at/index.md) return, so `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](https://json.nlohmann.me/api/basic_json_view/basic_json_view/index.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==`](https://json.nlohmann.me/api/basic_json_view/operator_eq/index.md).
|
||||
|
||||
[`is_json_view<T>`](https://json.nlohmann.me/api/is_json_view/index.md) tells views from other types, for constraints that cannot be written as a `basic_json_view<...>` parameter.
|
||||
|
||||
## A `from_json()` that reads views
|
||||
|
||||
[`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) on a view calls `T`'s `from_json()` with the view itself if that `from_json()` accepts views, and with [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) otherwise. The `from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/index.md), [`NLOHMANN_DEFINE_DERIVED_TYPE_*`](https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/index.md), and [`NLOHMANN_JSON_SERIALIZE_ENUM`](https://json.nlohmann.me/api/macros/nlohmann_json_serialize_enum/index.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 `materialize().get<T>()`.
|
||||
|
||||
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
|
||||
|
||||
```
|
||||
template < typename J, typename std::enable_if <
|
||||
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
|
||||
void from_json(const J& j, person& p)
|
||||
{
|
||||
p.name = j.at("name").template get<std::string>();
|
||||
p.age = j.at("age").template get<int>();
|
||||
}
|
||||
```
|
||||
|
||||
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`, `contains()`, `value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained `template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in [arbitrary type conversions](https://json.nlohmann.me/features/arbitrary_types/index.md), is never called with a view, as it might use `basic_json` functions a view does not have; such a type is converted through `materialize()`. A `from_json()` for `json` only is converted through `materialize()` as well.
|
||||
|
||||
Reading the view and `materialize().get<T>()` give the same result, except for an object with a repeated key; see [Duplicate keys](https://json.nlohmann.me/features/json_view/differences/#duplicate-keys).
|
||||
|
||||
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](https://json.nlohmann.me/features/json_view/index.md) - overview
|
||||
- [`is_json_view`](https://json.nlohmann.me/api/is_json_view/index.md) - type trait to detect views
|
||||
- [`view_mode`](https://json.nlohmann.me/api/view_mode/index.md) - read-only and editable views
|
||||
- [Reading values](https://json.nlohmann.me/features/json_view/reading/index.md) - what `get<T>()` converts without materializing
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0 unreleased.
|
||||
@@ -0,0 +1,103 @@
|
||||
# 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
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// a configuration a service parses once and then caches as an image, so
|
||||
// that later requests can load() it instead of parsing the text again
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]})";
|
||||
const json_document config = json_document::parse(text);
|
||||
|
||||
// save() turns the parsed document into a byte buffer: a 64-byte header,
|
||||
// the node index, the source text, and the decoded strings
|
||||
const std::vector<std::uint8_t> image = config.save();
|
||||
std::cout << image.size() << '\n';
|
||||
|
||||
// the same document always saves to the same bytes
|
||||
std::cout << (image == json_document::parse(text).save()) << '\n';
|
||||
|
||||
// loading the image back needs no parsing, yet dumps exactly what
|
||||
// parsing the text produced
|
||||
const json_document reloaded = json_document::load(image);
|
||||
std::cout << (reloaded.root().dump() == config.root().dump()) << '\n';
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
316
|
||||
true
|
||||
true
|
||||
|
||||
```
|
||||
|
||||
!!! 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.
|
||||
@@ -0,0 +1,34 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/images/" rel="canonical"><link href=../editing/ rel=prev><link href=../../../integration/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Images - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#images class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Images </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 Line truncated
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><iostream></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><string></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><vector></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_document</span><span class=p>;</span>
|
||||
|
||||
<span class=kt>int</span><span class=w> </span><span class=nf>main</span><span class=p>()</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>boolalpha</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// a configuration a service parses once and then caches as an image, so</span>
|
||||
<span class=w> </span><span class=c1>// that later requests can load() it instead of parsing the text again</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>config</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// save() turns the parsed document into a byte buffer: a 64-byte header,</span>
|
||||
<span class=w> </span><span class=c1>// the node index, the source text, and the decoded strings</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o><</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>></span><span class=w> </span><span class=n>image</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>config</span><span class=p>.</span><span class=n>save</span><span class=p>();</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>image</span><span class=p>.</span><span class=n>size</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// the same document always saves to the same bytes</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=p>(</span><span class=n>image</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>).</span><span class=n>save</span><span class=p>())</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// loading the image back needs no parsing, yet dumps exactly what</span>
|
||||
<span class=w> </span><span class=c1>// parsing the text produced</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>reloaded</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>load</span><span class=p>(</span><span class=n>image</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=p>(</span><span class=n>reloaded</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>dump</span><span class=p>()</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=n>config</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>dump</span><span class=p>())</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=mi>316</span>
|
||||
<span class=kc>true</span>
|
||||
<span class=kc>true</span>
|
||||
</code></pre></div> </details> <div class="admonition warning"> <p class=admonition-title>Experimental</p> <p>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, <code>save()</code> and <code>load()</code> 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 <em>same</em> build, not as a long-term storage format; keep the original <abbr title="JavaScript Object Notation">JSON</abbr> text if a saved document needs to be readable by a future library version.</p> </div> <h2 id=credits>Credits<a class=headerlink href=#credits title="Permanent link">¶</a></h2> <p>The idea of a document you can read without parsing comes from zero-copy formats such as <a href="https://github.com/google/flatbuffers">FlatBuffers</a> and <a href="https://github.com/yandex/yaff">YaFF</a>; the check <a href=../../../api/basic_json_document/load/ ><code>load()</code></a> runs follows the idea of FlatBuffers' Verifier. No code is taken from either.</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li><a href=../ >Zero-copy <abbr title="JavaScript Object Notation">JSON</abbr> views</a> - overview</li> <li><a href=../../../api/basic_json_document/save/ ><code>save()</code></a> - write a document as an image</li> <li><a href=../../../api/basic_json_document/load/ ><code>load()</code></a> - read an image back into a document</li> </ul> <h2 id=version-history>Version history<a class=headerlink href=#version-history title="Permanent link">¶</a></h2> <ul> <li>Added in version 3.13.0 <span class=unreleased-version title="Not part of a release yet; the latest release is 3.12.0.">unreleased</span>.</li> </ul> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date" title="November 9, 2026 07:12:00 UTC">November 9, 2026</span> </span> </aside> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <nav class="md-footer__inner md-grid" aria-label=Footer> <a href=../editing/ class="md-footer__link md-footer__link--prev" aria-label="Previous: Editing a document"> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </div> <div class=md-footer__title> <span class=md-footer__direction> Previous </span> <div class=md-ellipsis> Editing a document </div> </div> </a> <a href=../../../integration/ class="md-footer__link md-footer__link--next" aria-label="Next: Integration"> <div class=md-footer__title> <span class=md-footer__direction> Next </span> <div class=md-ellipsis> Integration </div> </div> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg> </div> </a> </nav> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> <div class=md-copyright__highlight> Copyright © 2013-2026 Niels Lohmann </div> </div> <div class=md-social> <a href="https://github.com/nlohmann" target="_blank" rel="noopener" title="github.com" class="md-social__link"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3Line truncated
|
||||
@@ -0,0 +1,78 @@
|
||||
# Images
|
||||
|
||||
[`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md) writes a document as an *image*: a byte buffer that [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) still has to scan the whole input, [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/save/index.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()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) takes an [`image_check`](https://json.nlohmann.me/api/basic_json_document/load/#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()`](https://json.nlohmann.me/api/basic_json_document/save/index.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](https://json.nlohmann.me/api/basic_json_document/load/#notes) for exactly what each level does and does not guarantee.
|
||||
|
||||
Example: cache a parsed configuration as an image
|
||||
|
||||
```
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// a configuration a service parses once and then caches as an image, so
|
||||
// that later requests can load() it instead of parsing the text again
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]})";
|
||||
const json_document config = json_document::parse(text);
|
||||
|
||||
// save() turns the parsed document into a byte buffer: a 64-byte header,
|
||||
// the node index, the source text, and the decoded strings
|
||||
const std::vector<std::uint8_t> image = config.save();
|
||||
std::cout << image.size() << '\n';
|
||||
|
||||
// the same document always saves to the same bytes
|
||||
std::cout << (image == json_document::parse(text).save()) << '\n';
|
||||
|
||||
// loading the image back needs no parsing, yet dumps exactly what
|
||||
// parsing the text produced
|
||||
const json_document reloaded = json_document::load(image);
|
||||
std::cout << (reloaded.root().dump() == config.root().dump()) << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
316
|
||||
true
|
||||
true
|
||||
```
|
||||
|
||||
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()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from either.
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](https://json.nlohmann.me/features/json_view/index.md) - overview
|
||||
- [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md) - write a document as an image
|
||||
- [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) - read an image back into a document
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0 unreleased.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/" rel="canonical"><link href=../types/template_parameters/ rel=prev><link href=../../integration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Zero-copy JSON views - JSON for Modern C++</title><link rel=stylesheet href=../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../css/custom.css><link rel=stylesheet href=../../css/brand.css><link rel=stylesheet href=../../css/fig-sax.css><link rel=stylesheet href=../../css/fig-jsonview.css><link rel=stylesheet href=../../css/fig-exceptions.css><link rel=stylesheet href=../../css/fig-pointer.css><link rel=stylesheet href=../../css/fig-charts.css><link rel=stylesheet href=../../css/fig-integration.css><link rel=stylesheet href=../../css/fig-macros.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#zero-copy-json-views class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Zero-copy JSON views </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9Line truncated
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/" rel="canonical"><link href=../types/template_parameters/ rel=prev><link href=lifetime/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Zero-copy JSON views - JSON for Modern C++</title><link rel=stylesheet href=../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../css/custom.css><link rel=stylesheet href=../../css/brand.css><link rel=stylesheet href=../../css/fig-sax.css><link rel=stylesheet href=../../css/fig-jsonview.css><link rel=stylesheet href=../../css/fig-exceptions.css><link rel=stylesheet href=../../css/fig-pointer.css><link rel=stylesheet href=../../css/fig-charts.css><link rel=stylesheet href=../../css/fig-integration.css><link rel=stylesheet href=../../css/fig-macros.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#zero-copy-json-views class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Zero-copy JSON views </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61Line truncated
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_document</span><span class=p>;</span>
|
||||
@@ -16,159 +16,4 @@
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=kc>true</span>
|
||||
<span class=p>{</span><span class=nt>"numbers"</span><span class=p>:[</span><span class=mi>1</span><span class=p>,</span><span class=mi>2</span><span class=p>,</span><span class=mi>3</span><span class=p>],</span><span class=nt>"pi"</span><span class=p>:</span><span class=mf>3.14</span><span class=p>}</span>
|
||||
</code></pre></div> </details> <p><a href=../../api/ordered_json_document/ ><code>ordered_json_document</code></a> is the equivalent for <a href=../../api/ordered_json/ ><code>ordered_json</code></a>, just as <a href=../../api/ordered_json/ ><code>ordered_json</code></a> is to <a href=../../api/json/ ><code>json</code></a>.</p> <h2 id=ownership-and-lifetime>Ownership and lifetime<a class=headerlink href=#ownership-and-lifetime title="Permanent link">¶</a></h2> <p>A document either <strong>borrows</strong> the text it was parsed from, or <strong>owns</strong> its own copy of it; call <a href=../../api/basic_json_document/owns_source/ ><code>owns_source()</code></a> to find out which happened. <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> decides this from the value category and type of its argument (an lvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> is borrowed; an rvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> is moved in, owned without a copy; a stream is read into an owned buffer; and so on — see <a href=../../api/basic_json_document/parse/#notes><code>parse</code>'s Notes</a> for the full table). <a href=../../api/basic_json_document/parse_copy/ ><code>parse_copy()</code></a> always owns a copy, regardless of the input.</p> <div class="admonition warning"> <p class=admonition-title>A borrowed document depends on your buffer</p> <p>If a document borrows its text, that text <strong>must outlive the document</strong> (and every view taken from it) and must stay <strong>unmodified</strong> 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 <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code>.</p> </div> <p>A view is valid only while all of the following hold:</p> <ul> <li>the document is alive,</li> <li>the document has not been re-parsed since the view was taken (with <a href=../../api/basic_json_document/read/ ><code>read()</code></a> or <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> into it),</li> <li>the document has not had <a href=../../api/basic_json_document/shrink_to_fit/ ><code>shrink_to_fit()</code></a> called on it since, and</li> <li>if the document borrows its source text, that text is still alive and unmodified.</li> </ul> <p>Moving the <code>basic_json_document<></code> itself is fine and does <strong>not</strong> invalidate its views: the index is a separate heap allocation that keeps its address across the move. Take a fresh view from <a href=../../api/basic_json_document/root/ ><code>root()</code></a> whenever any of the other conditions above was not met.</p> <div class=nj-fig-wrap> <p><svg class="nj-fig nj-jsonview " viewbox="0 0 840 572" role=img aria-labelledby="fig-jvt-title fig-jvt-title-d" xmlns=http://www.w3.org/2000/svg> <title id=fig-jvt-title>When a json_view becomes invalid</title> <desc id=fig-jvt-title-d>Four timelines. First: a document borrows a std::string; when the string is destroyed, the document and every view taken from it dangle. Second: doc.read() re-parses the document; views taken before are invalid, a fresh view from root() is valid. Third: doc.shrink_to_fit() has the same effect. Fourth: moving the document does not invalidate its views; destroying the moved-to document does.</desc> <defs> <marker id=jvt-bad viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> </defs> <rect x=210 y=12 width=34 height=12 rx=3 class="nj-jsonview-bar nj-jsonview-c-struct"/> <text class=nj-jsonview-note x=252 y=23 xml:space=preserve>alive / valid</text> <rect class=nj-jsonview-bad x=370 y=12 width=34 height=12 rx=3 /> <text class=nj-jsonview-note x=412 y=23 xml:space=preserve>dangling: using it is undefined behavior</text> <path class="nj-jsonview-arr nj-jsonview-c-line" d="M210 44 L820 44"/> <text class=nj-jsonview-tiny x=820 y=38 text-anchor=end xml:space=preserve>time →</text> <text class="nj-jsonview-h nj-jsonview-sm2" x=16 y=74 xml:space=preserve>The borrowed text is destroyed</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=198 y=99 text-anchor=end xml:space=preserve>std::string text</text> <rect x=210 y=87 width=260 height=16 rx=3 class="nj-jsonview-bar nj-jsonview-c-struct"/> <text class="nj-jsonview-mono nj-jsonview-sm" x=198 y=127 text-anchor=end xml:space=preserve>json_document doc</text> <rect x=270 y=115 width=200 height=16 rx=3 class="nj-jsonview-bar nj-jsonview-c-struct"/> <rect class=nj-jsonview-bad x=470 y=115 width=350 height=16 rx=3 /> <text class="nj-jsonview-mono nj-jsonview-sm" x=198 y=155 text-anchor=endLine truncated
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><string></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><utility></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_document</span><span class=p>;</span>
|
||||
|
||||
<span class=kt>int</span><span class=w> </span><span class=nf>main</span><span class=p>()</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>boolalpha</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// BORROWED: doc only points into `text`; `text` must outlive `doc`</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"a": 1}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>doc</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>owns_source</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span><span class=w> </span><span class=c1>// false</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// a view is valid as long as the document is alive, has not been</span>
|
||||
<span class=w> </span><span class=c1>// re-parsed (read()) or shrunk (shrink_to_fit()), and -- if borrowed --</span>
|
||||
<span class=w> </span><span class=c1>// the source text is alive</span>
|
||||
<span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_view</span><span class=w> </span><span class=n>v</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>();</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>v</span><span class=p>.</span><span class=n>is_object</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// re-parsing the SAME document invalidates views taken before the call;</span>
|
||||
<span class=w> </span><span class=c1>// `v` above must not be used after this line</span>
|
||||
<span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>read</span><span class=p>(</span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>[1, 2, 3]</span><span class=dl>)</span><span class=s>"</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>v</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>();</span><span class=w> </span><span class=c1>// take a fresh view instead</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>v</span><span class=p>.</span><span class=n>is_array</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// moving the document does not invalidate views: the node index is</span>
|
||||
<span class=w> </span><span class=c1>// heap-allocated and does not move with the document object</span>
|
||||
<span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>moved</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>move</span><span class=p>(</span><span class=n>doc</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>v</span><span class=p>.</span><span class=n>is_array</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=kc>false</span>
|
||||
<span class=kc>true</span>
|
||||
<span class=kc>true</span>
|
||||
<span class=kc>true</span>
|
||||
</code></pre></div> </details> <h2 id=what-is-the-same-as-parse>What is the same as <code>parse()</code><a class=headerlink href=#what-is-the-same-as-parse title="Permanent link">¶</a></h2> <ul> <li><strong>Accept/reject.</strong> <a href=../../api/basic_json_document/accept/ ><code>accept()</code></a> and <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> accept and reject exactly the same inputs as <a href=../../api/basic_json/accept/ ><code>basic_json::accept()</code></a>/<a href=../../api/basic_json/parse/ ><code>basic_json::parse()</code></a>, with the same <code>ignore_comments</code> and <code>ignore_trailing_commas</code> options.</li> <li><strong>Errors.</strong> 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. <code class=highlight><span class=n>allow_exceptions</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=nb>false</span></code> skips the replay and gives a <a href=../../api/basic_json_document/is_discarded/ >discarded</a> document instead of throwing, just as it gives a discarded value for <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code>.</li> <li><strong>Number classification.</strong> An integer literal that does not fit into the 64-bit integer type becomes a floating-point value, exactly as it does for <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code>.</li> <li> <p><strong>Macros.</strong> <a href=../../api/macros/json_strict_nul_handling/ ><code>JSON_STRICT_NUL_HANDLING</code></a> and <a href=../../api/macros/json_noexception/ ><code>JSON_NOEXCEPTION</code></a>/<a href=../../api/macros/json_throw_user/ ><code>JSON_THROW_USER</code></a> behave the same way they do for <code><nlohmann/json.hpp></code>. 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 <code class=highlight><span class=n>basic_json</span></code>:</p> <ul> <li>With <a href=../../api/macros/json_diagnostics/ ><code>JSON_DIAGNOSTICS</code></a>, an exception names the <abbr title="JavaScript Object Notation">JSON</abbr> Pointer path of the value within the document.</li> <li>With <a href=../../api/macros/json_diagnostic_positions/ ><code>JSON_DIAGNOSTIC_POSITIONS</code></a>, an exception names the bytes of the value in the source text.</li> </ul> <p>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: <code>key()</code> of an iterator over an array or a primitive value (<a href=../../home/exceptions/#jsonexceptioninvalid_iterator207><code>invalid_iterator.207</code></a>), because a view's iterator does not know the value it iterates over.</p> <p>The other macros only affect the values <code>materialize()</code> produces, through <code>BasicJsonType</code>.</p> </li> </ul> <h2 id=what-is-different>What is different<a class=headerlink href=#what-is-different title="Permanent link">¶</a></h2> <ul> <li><strong>Only 64-bit integers.</strong> <code>basic_json_document<BasicJsonType></code> requires <code>BasicJsonType::number_integer_t</code> and <code>number_unsigned_t</code> to both be 64 bits wide; this is a compile-time <code class=highlight><span class=k>static_assert</span></code>.</li> <li><strong>A 4 <abbr title="Gibibyte (2^30 bytes)">GiB</abbr> input limit.</strong> An input of 4294967280 bytes (4 <abbr title="Gibibyte (2^30 bytes)">GiB</abbr> minus 16 bytes) or more throws <a href=../../home/exceptions/#jsonexceptionout_of_range416><code>out_of_range.416</code></a>, a limit <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code> 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.</li> <li><strong>A stream is always read to its end.</strong> There is no partial/streaming read of an <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>istream</span></code>: the stream is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and was about 6 to 12 % faster than parsLine truncated
|
||||
<span class=n>json_view</span><span class=w> </span><span class=n>last</span><span class=p>;</span>
|
||||
<span class=k>for</span><span class=w> </span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=k>auto</span><span class=w> </span><span class=n>item</span><span class=w> </span><span class=o>:</span><span class=w> </span><span class=n>view</span><span class=p>.</span><span class=n>items</span><span class=p>())</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=k>if</span><span class=w> </span><span class=p>(</span><span class=n>item</span><span class=p>.</span><span class=n>key</span><span class=p>()</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=s>"a"</span><span class=p>)</span>
|
||||
<span class=w> </span><span class=p>{</span>
|
||||
<span class=w> </span><span class=n>last</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>item</span><span class=p>.</span><span class=n>value</span><span class=p>();</span>
|
||||
<span class=w> </span><span class=p>}</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>If you cannot trust the source text to have unique keys, check <a href=../../api/basic_json_view/size/ ><code>size()</code></a> against the number of distinct keys, or reject duplicates when iterating.</p> <h2 id=getting-values-out-without-copying>Getting values out without copying<a class=headerlink href=#getting-values-out-without-copying title="Permanent link">¶</a></h2> <p><a href=../../api/basic_json_view/get/ ><code>get<T>()</code></a> converts most <code>T</code> directly from the flat index, without ever building a <code>basic_json</code> value for the conversion: <code class=highlight><span class=kt>bool</span></code>, arithmetic types, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>nullptr_t</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>/other <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>basic_string</span></code>s (copied once), <code>basic_json_view</code> itself, the standard containers and other types the library converts (<code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>vector</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>map</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>list</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>array</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>pair</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>tuple</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>optional</span></code>, ...), and the types of the <code>NLOHMANN_DEFINE_TYPE_*</code> and <code>NLOHMANN_JSON_SERIALIZE_ENUM</code> macros. Types whose <code>from_json()</code> is written for <code>basic_json</code> only go through <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a><code>.get<T>()</code> instead: the subtree is built into a real <code>basic_json</code> value first, exactly as <a href=../../api/basic_json/parse/ ><code>parse()</code></a> would, and converted from there. See <a href=#writing-generic-code>Writing generic code</a> for how to write a <code>from_json()</code> that reads views.</p> <p>Two conversions never copy at all:</p> <ul> <li><a href=../../api/basic_json_view/get_string/ ><code>get_string()</code></a> (equivalently, <code class=highlight><span class=n>get</span><span class=o><</span><span class=n>string_view_t</span><span class=o>></span><span class=p>()</span></code>) returns a string as a <code>string_view_t</code> pointing into the document's <a href=../../api/basic_json_document/source/ ><code>source()</code></a> text — or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of allocating a new <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>.</li> <li><a href=../../api/basic_json_view/number_token/ ><code>number_token()</code></a> returns a number exactly as it was written in the source, e.g. <code class=highlight><span class=s>"1.50"</span></code>, <code class=highlight><span class=s>"1E2"</span></code>, or an integer with more digits than any number type holds, instead of rounding it into a <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> the way <code class=highlight><span class=n>get</span><span class=o><</span><span class=n>T</span><span class=o>></span><span class=p>()</span></code> (and <a href=../../api/basic_json/parse/ ><code>basic_json::parse()</code></a>) would.</li> </ul> <p>Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.</p> <h3 id=string_view-members><code>string_view</code> members<a class=headerlink href=#string_view-members title="Permanent link">¶</a></h3> <p>A struct whose member is a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code> (C++17) can be decoded from a view with the <code>NLOHMANN_DEFINE_TYPE_*</code> 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 <a href=#ownership-and-lifetime>borrows its source text</a> (as <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> does for an lvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>, a <code class=highlight><span class=n>std</spLine truncated
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string_view</span><span class=w> </span><span class=n>name</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=kt>int</span><span class=w> </span><span class=n>count</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>NLOHMANN_DEFINE_TYPE_INTRUSIVE</span><span class=p>(</span><span class=n>record</span><span class=p>,</span><span class=w> </span><span class=n>name</span><span class=p>,</span><span class=w> </span><span class=n>count</span><span class=p>)</span>
|
||||
<span class=p>};</span>
|
||||
|
||||
<span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"name": "widget", "count": 3}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=k>const</span><span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>doc</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span><span class=w> </span><span class=c1>// borrows text</span>
|
||||
<span class=k>const</span><span class=w> </span><span class=n>record</span><span class=w> </span><span class=n>r</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>get</span><span class=o><</span><span class=n>record</span><span class=o>></span><span class=p>();</span><span class=w> </span><span class=c1>// r.name points into text, valid while text and doc are</span>
|
||||
</code></pre></div> <div class="admonition warning"> <p class=admonition-title>Decoding into a <code>string_view</code> from a <code>json</code> value dangles at once</p> <p>The same struct also compiles with <code class=highlight><span class=n>json</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>).</span><span class=n>get</span><span class=o><</span><span class=n>record</span><span class=o>></span><span class=p>()</span></code>, but there <code>name</code> points into the temporary <code>json</code> value, which is destroyed at the end of the statement. Use a named <a href=../../api/json_document/ ><code>json_document</code></a>, as above, to decode such a struct.</p> </div> <h2 id=writing-generic-code>Writing generic code<a class=headerlink href=#writing-generic-code title="Permanent link">¶</a></h2> <p>A view's type names its <code>BasicJsonType</code> and its <a href=../../api/view_mode/ ><code>view_mode</code></a>: <a href=../../api/json_view/ ><code>json_view</code></a> is <code class=highlight><span class=n>basic_json_view</span><span class=o><</span><span class=n>json</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>read_only</span><span class=o>></span></code>, and <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a> is <code class=highlight><span class=n>basic_json_view</span><span class=o><</span><span class=n>json</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>editable</span><span class=o>></span></code>. 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 <code>BasicJsonType</code>, so a function written for one shortcut accepts few other views:</p> <div class=highlight><pre><span></span><code><span class=kt>void</span><span class=w> </span><span class=nf>print_names</span><span class=p>(</span><span class=n>json_view</span><span class=w> </span><span class=n>v</span><span class=p>);</span><span class=w> </span><span class=c1>// accepts neither an ordered_json_view nor a json_editable_view</span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=nf>print_names</span><span class=p>(</span><span class=n>json_editable_view</span><span class=w> </span><span class=n>v</span><span class=p>);</span><span class=w> </span><span class=c1>// also accepts a json_view, but no ordered_json_view</span>
|
||||
</code></pre></div> <p>This is the same pitfall as a <code>to_json</code>/<code>from_json</code> written for <code>json</code> instead of any <code>BasicJsonType</code>, which then does not work for <code>ordered_json</code>. The shortcuts are for declaring documents and views; a function that takes a view names the views it accepts with a template:</p> <div class=highlight><pre><span></span><code><span class=c1>// views of editable documents only</span>
|
||||
<span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>f</span><span class=p>(</span><span class=n>basic_json_view</span><span class=o><</span><span class=n>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>editable</span><span class=o>></span><span class=w> </span><span class=n>v</span><span class=p>);</span>
|
||||
|
||||
<span class=c1>// views of read-only documents only</span>
|
||||
<span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>f</span><span class=p>(</span><span class=n>basic_json_view</span><span class=o><</span><span class=n>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>read_only</span><span class=o>></span><span class=w> </span><span class=n>v</span><span class=p>);</span>
|
||||
|
||||
<span class=c1>// views of either document type</span>
|
||||
<span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=w> </span><span class=n>Mode</span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>f</span><span class=p>(</span><span class=n>basic_json_view</span><span class=o><</span><span class=n>BasicJsonType</span><span class=p>,</span><span class=w> </span><span class=n>Mode</span><span class=o>></span><span class=w> </span><span class=n>v</span><span class=p>);</span>
|
||||
</code></pre></div> <p>Views are passed by value: they are two pointers, cheap to copy, like a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code>. A <code>const&</code> works as well, but a non-<code>const</code> reference does not bind to the views that <a href=../../api/basic_json_document/root/ ><code>root()</code></a>, <a href=../../api/basic_json_view/operator%5B%5D/ ><code>operator[]</code></a>, and <a href=../../api/basic_json_view/at/ ><code>at()</code></a> return, so <code class=highlight><span class=n>f</span><span class=p>(</span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>())</span></code> would not compile.</p> <p>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 <a href=../../api/basic_json_view/basic_json_view/ >converts</a> 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 <a href=../../api/basic_json_view/operator_eq/ ><code>operator==</code></a>.</p> <p><a href=../../api/is_json_view/ ><code>is_json_view<T></code></a> tells views from other types, for constraints that cannot be written as a <code>basic_json_view<...></code> parameter.</p> <h3 id=a-from_json-that-reads-views>A <code>from_json()</code> that reads views<a class=headerlink href=#a-from_json-that-reads-views title="Permanent link">¶</a></h3> <p><a href=../../api/basic_json_view/get/ ><code>get<T>()</code></a> on a view calls <code>T</code>'s <code>from_json()</code> with the view itself if that <code>from_json()</code> accepts views, and with <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a> otherwise. The <code>from_json()</code> functions of the <a href=../../api/macros/nlohmann_define_type_intrusive/ ><code>NLOHMANN_DEFINE_TYPE_*</code></a>, <a href=../../api/macros/nlohmann_define_derived_type/ ><code>NLOHMANN_DEFINE_DERIVED_TYPE_*</code></a>, and <a href=../../api/macros/nlohmann_json_serialize_enum/ ><code>NLOHMANN_JSON_SERIALIZE_ENUM</code></a> 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 <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>get</span><span class=o><</span><span class=n>T</span><span class=o>></span><span class=p>()</span></code>.</p> <p>A hand-written <code>from_json()</code> reads views if it is constrained to <code>json</code> values and views:</p> <div class=highlight><pre><span></span><code><span class=k>template</span><span class=w> </span><span class=o><</span><span class=w> </span><span class=k>typename</span><span class=w> </span><span class=nc>J</span><span class=p>,</span><span class=w> </span><span class=k>typename</span><span class=w> </span><span class=nc>std</span><span class=o>::</span><span class=n>enable_if</span><span class=w> </span><span class=o><</span>
|
||||
<span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>is_json_view</span><span class=o><</span><span class=n>J</span><span class=o>>::</span><span class=n>value</span><span class=w> </span><span class=o>||</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>is_same</span><span class=o><</span><span class=n>J</span><span class=p>,</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=o>>::</span><span class=n>value</span><span class=p>,</span><span class=w> </span><span class=kt>int</span><span class=w> </span><span class=o>>::</span><span class=n>type</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>0</span><span class=w> </span><span class=o>></span>
|
||||
<span class=kt>void</span><span class=w> </span><span class=n>from_json</span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>J</span><span class=o>&</span><span class=w> </span><span class=n>j</span><span class=p>,</span><span class=w> </span><span class=n>person</span><span class=o>&</span><span class=w> </span><span class=n>p</span><span class=p>)</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>p</span><span class=p>.</span><span class=n>name</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>j</span><span class=p>.</span><span class=n>at</span><span class=p>(</span><span class=s>"name"</span><span class=p>).</span><span class=k>template</span><span class=w> </span><span class=n>get</span><span class=o><</span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=o>></span><span class=p>();</span>
|
||||
<span class=w> </span><span class=n>p</span><span class=p>.</span><span class=n>age</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>j</span><span class=p>.</span><span class=n>at</span><span class=p>(</span><span class=s>"age"</span><span class=p>).</span><span class=k>template</span><span class=w> </span><span class=n>get</span><span class=o><</span><span class=kt>int</span><span class=o>></span><span class=p>();</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Its body may only use what views and <code>basic_json</code> have in common: the type checks, <code>at()</code>, <code>operator[]</code>, <code>contains()</code>, <code>value()</code>, <code>size()</code>, iteration and <code>items()</code>, and <code>get<T>()</code>/<code>get_to()</code>. An unconstrained <code class=highlight><span class=k>template</span><span class=o><</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>></span><span class=w> </span><span class=kt>void</span><span class=w> </span><span class=n>from_json</span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>BasicJsonType</span><span class=o>&</span><span class=w> </span><span class=n>j</span><span class=p>,</span><span class=w> </span><span class=n>person</span><span class=o>&</span><span class=w> </span><span class=n>p</span><span class=p>)</span></code>, as in <a href=../arbitrary_types/ >arbitrary type conversions</a>, is never called with a view, as it might use <code>basic_json</code> functions a view does not have; such a type is converted through <code>materialize()</code>. A <code>from_json()</code> for <code>json</code> only is converted through <code>materialize()</code> as well.</p> <p>One thing differs when a <code>from_json()</code> reads the view: members looked up by name (<code>at()</code>, <code>value()</code>, ... and thus the macros) find the <strong>first</strong> member with a repeated key, see <a href=#duplicate-keys>Duplicate keys</a>; <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>get</span><span class=o><</span><span class=n>T</span><span class=o>></span><span class=p>()</span></code> would see the last one. Containers built from an object, such as <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>map</span></code>, keep the <strong>last</strong> value either way, as <a href=../../api/basic_json/parse/ ><code>parse()</code></a> does.</p> <p><code>to_json()</code> is unaffected: it writes <code>basic_json</code> values, never views.</p> <h2 id=writing-a-view-back>Writing a view back<a class=headerlink href=#writing-a-view-back title="Permanent link">¶</a></h2> <p><a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> serializes a view directly from the flat index, without ever building a <code>basic_json</code> value. An object's members are written in document order, not sorted by key, and <em>every</em> occurrence of a repeated key is written, not only the last one — the same two ways <a href=#what-is-different>iteration</a> already differs from a <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d value, see <a href=#duplicate-keys>Duplicate keys</a>. <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>dump</span><span class=p>()</span></code> gives a different result in both respects for a <code>json_view</code>.</p> <p>By default, numbers are written the way <a href=../../api/basic_json/dump/ ><code>basic_json::dump()</code></a> would. <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> instead copies every number exactly as it was written in the source text — a price like <code class=highlight><span class=mf>19.90</span></code>, a long order or account ID with more digits than any number type holds, or a high-precision coordinate — something <code>basic_json</code> cannot do at all, since parsing already reduces a number to its parsed <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> value.</p> <p><a href=../../api/basic_json_view/operator_ltlt/ ><code>operator<<</code></a> writes a view to a stream the way <code>basic_json</code>'s does, using the stream's <code>width</code>/<code>fill</code> for indentation.</p> <h2 id=editing-a-document>Editing a document<a class=headerlink href=#editing-a-document title="Permanent link">¶</a></h2> <p>Everything above is read-only: a <code>json_document</code>/<code>json_view</code> lets you look at a parsed text without copying it, but not change it. <a href=../../api/basic_json_document/ ><code>basic_json_document<BasicJsonType, view_mode::editable></code></a> — more conveniently spelled <a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> or <a href=../../api/ordered_json_editable_document/ ><code>ordered_json_editable_document</code></a> — also lets you <a href=../../api/basic_json_document/set/ ><code>set</code></a> a value, <a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a> onto or <a href=../../api/basic_json_document/insert/ ><code>insert</code></a> into an array, and <a href=../../api/basic_json_document/erase/ ><code>erase</code></a> an object member or an array element, still withoutLine truncated
|
||||
"name":"cache",
|
||||
"port":6379,
|
||||
"price":<span class=nj-jsonview-bad>19.9</span>,
|
||||
"retries":3}</pre></div> <div><p class=nj-jsonview-cmp-h><code>ordered_json</code></p><p class=nj-jsonview-cmp-v>key order kept, numbers rewritten</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class=nj-jsonview-bad>19.9</span>,
|
||||
"retries":3}</pre></div> <div class=nj-jsonview-cmp--best><p class=nj-jsonview-cmp-h><code>json_editable_document</code></p><p class=nj-jsonview-cmp-v>order and spelling kept</p><pre>{"name":"cache",
|
||||
"host":"db2",
|
||||
"port":6379,
|
||||
"price":<span class=nj-jsonview-ok>19.90</span>,
|
||||
"retries":3}</pre></div> </div> <p>An editable document keeps both. <a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document writes members in document order — a member <a href=../../api/basic_json_document/set/ ><code>set</code></a> added goes at the end, exactly where it was inserted, and an <a href=../../api/basic_json_document/erase/ ><code>erase</code></a>d member simply leaves a gap: everything around it keeps its place — and <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> keeps the exact spelling of every number an edit did not itself touch; a number an edit <em>did</em> touch is written the way <a href=../../api/basic_json/dump/ ><code>BasicJsonType::dump()</code></a> would write it, since there is no source spelling for a brand new value.</p> <details class=example> <summary>Example: patch a configuration, keeping member order and number spellings</summary> <div class=highlight><pre><span></span><code><span class=cp>#include</span><span class=w> </span><span class=cpf><iostream></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=p>;</span>
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_editable_document</span><span class=p>;</span>
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_editable_view</span><span class=p>;</span>
|
||||
|
||||
<span class=kt>int</span><span class=w> </span><span class=nf>main</span><span class=p>()</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=c1>// a configuration file, as it might be read from disk</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=s>"</span><span class=se>\n\n</span><span class=s>"</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// patch two fields -- "price" is never touched</span>
|
||||
<span class=w> </span><span class=n>json_editable_document</span><span class=w> </span><span class=n>doc</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_editable_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>set</span><span class=p>(</span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>(),</span><span class=w> </span><span class=s>"host"</span><span class=p>,</span><span class=w> </span><span class=s>"db2"</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>set</span><span class=p>(</span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>(),</span><span class=w> </span><span class=s>"retries"</span><span class=p>,</span><span class=w> </span><span class=mi>3</span><span class=p>);</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// member order (the new member at the end) and the untouched number's</span>
|
||||
<span class=w> </span><span class=c1>// exact spelling survive</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>dump</span><span class=p>(</span><span class=mi>-1</span><span class=p>,</span><span class=w> </span><span class=sc>' '</span><span class=p>,</span><span class=w> </span><span class=nb>false</span><span class=p>,</span><span class=w> </span><span class=n>json_editable_view</span><span class=o>::</span><span class=n>number_format</span><span class=o>::</span><span class=n>source</span><span class=p>)</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// the same patch on a plain json value: keys are sorted (object_t is a</span>
|
||||
<span class=w> </span><span class=c1>// std::map), and "price" is rewritten even though the patch never</span>
|
||||
<span class=w> </span><span class=c1>// touched it</span>
|
||||
<span class=w> </span><span class=n>json</span><span class=w> </span><span class=n>plain</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>plain</span><span class=p>[</span><span class=s>"host"</span><span class=p>]</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=s>"db2"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>plain</span><span class=p>[</span><span class=s>"retries"</span><span class=p>]</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>3</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>plain</span><span class=p>.</span><span class=n>dump</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=p>{</span><span class=nt>"name"</span><span class=p>:</span><span class=w> </span><span class=s2>"cache"</span><span class=p>,</span><span class=w> </span><span class=nt>"host"</span><span class=p>:</span><span class=w> </span><span class=s2>"db1"</span><span class=p>,</span><span class=w> </span><span class=nt>"port"</span><span class=p>:</span><span class=w> </span><span class=mi>6379</span><span class=p>,</span><span class=w> </span><span class=nt>"price"</span><span class=p>:</span><span class=w> </span><span class=mf>19.90</span><span class=p>}</span>
|
||||
|
||||
<span class=p>{</span><span class=nt>"name"</span><span class=p>:</span><span class=s2>"cache"</span><span class=p>,</span><span class=nt>"host"</span><span class=p>:</span><span class=s2>"db2"</span><span class=p>,</span><span class=nt>"port"</span><span class=p>:</span><span class=mi>6379</span><span class=p>,</span><span class=nt>"price"</span><span class=p>:</span><span class=mf>19.90</span><span class=p>,</span><span class=nt>"retries"</span><span class=p>:</span><span class=mi>3</span><span class=p>}</span>
|
||||
<span class=p>{</span><span class=nt>"host"</span><span class=p>:</span><span class=s2>"db2"</span><span class=p>,</span><span class=nt>"name"</span><span class=p>:</span><span class=s2>"cache"</span><span class=p>,</span><span class=nt>"port"</span><span class=p>:</span><span class=mi>6379</span><span class=p>,</span><span class=nt>"price"</span><span class=p>:</span><span class=mf>19.9</span><span class=p>,</span><span class=nt>"retries"</span><span class=p>:</span><span class=mi>3</span><span class=p>}</span>
|
||||
</code></pre></div> </details> <h3 id=what-stays-valid-and-what-an-edit-costs>What stays valid, and what an edit costs<a class=headerlink href=#what-stays-valid-and-what-an-edit-costs title="Permanent link">¶</a></h3> <p>The source text itself is <strong>never written</strong>, 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 <a href=../../api/basic_json_view/ >view</a> taken before an edit, including a previously obtained <a href=../../api/basic_json_document/root/ ><code>root()</code></a>, 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 <strong>iterators</strong> 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 <a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a> 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 <a href=../../api/basic_json_view/get_string/ ><code>get_string()</code></a> is unaffected either way and stays valid across further edits. See <a href=../../api/basic_json_document/#edits><code>basic_json_document</code>'s Edits</a> for the details, and <a href=../../api/basic_json_document/set/#exception-safety><code>set</code>'s Exception safety</a> for what an edit guarantees if it throws (the <em>basic</em> guarantee, not the strong one <code>dump()</code> and the read-only functions provide). How edits are kept in the index is described in the <a href=../../home/architecture/#node-index-of-json-views>architecture overview</a>.</p> <p>Step through the edits of a small document (the node fields follow the <a href=../../home/architecture/#node-index-of-json-views>architecture overview</a>):</p> <div class=nj-jsonview-steps data-nj-jsonview-steps> <figure class=nj-jsonview-step data-label="After parsing"> <div class=nj-jsonview-scroll> <svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewbox="0 0 840 372" role=img aria-labelledby="fig-jve0-title fig-jve0-title-d" xmlns=http://www.w3.org/2000/svg> <title id=fig-jve0-title>Step 1 of 5: parsed</title> <desc id=fig-jve0-title-d>After parse: nine nodes, nothing owned yet. The dump with number_format::source is {"port":6379,"price":19.90,"tags":["eu","a\"b"]}.</desc> <defs> <marker id=jve0-line viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-own viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-struct viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-bad viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> </defs> <text class="nj-jsonview-note nj-jsonview-strong" x=16 y=22 xml:space=preserve>parsed index: fixed, never moves</text> <text class=nj-jsonview-note x=824 y=22 text-anchor=end xml:space=preserve>the source text is never written</text> <rect x=36 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-struct"/> <text class=nj-jsonview-tiny x=42 y=47 xml:space=preserve>0</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x=78 y=61 text-anchor=middle xml:space=preserve>object</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=78 y=77 text-anchor=middle xml:space=preserve>{ } 3</text> <rect x=124 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-key"/> <text class=nj-jsonview-tiny x=130 y=47 xml:space=preserve>1</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x=166 y=61 text-anchor=middle xml:space=preserve>key</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=166 y=77 text-anchor=middle xml:space=preserve>port</text> <rect x=212 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-num"/> <text class=nj-jsonview-tiny x=218 y=47 xml:space=preserve>2</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x=254 y=61 text-anchor=middle xml:space=preserve>uint</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=254 y=77 text-anchor=middle xml:space=preserve>6379</text> <rect x=300 Line truncated
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><iostream></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><string></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><vector></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_document</span><span class=p>;</span>
|
||||
|
||||
<span class=kt>int</span><span class=w> </span><span class=nf>main</span><span class=p>()</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>boolalpha</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// a configuration a service parses once and then caches as an image, so</span>
|
||||
<span class=w> </span><span class=c1>// that later requests can load() it instead of parsing the text again</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>config</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// save() turns the parsed document into a byte buffer: a 64-byte header,</span>
|
||||
<span class=w> </span><span class=c1>// the node index, the source text, and the decoded strings</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o><</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>></span><span class=w> </span><span class=n>image</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>config</span><span class=p>.</span><span class=n>save</span><span class=p>();</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>image</span><span class=p>.</span><span class=n>size</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// the same document always saves to the same bytes</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=p>(</span><span class=n>image</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>).</span><span class=n>save</span><span class=p>())</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// loading the image back needs no parsing, yet dumps exactly what</span>
|
||||
<span class=w> </span><span class=c1>// parsing the text produced</span>
|
||||
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>reloaded</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>load</span><span class=p>(</span><span class=n>image</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=p>(</span><span class=n>reloaded</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>dump</span><span class=p>()</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=n>config</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>dump</span><span class=p>())</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=mi>316</span>
|
||||
<span class=kc>true</span>
|
||||
<span class=kc>true</span>
|
||||
</code></pre></div> </details> <div class="admonition warning"> <p class=admonition-title>Experimental</p> <p>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, <code>save()</code> and <code>load()</code> 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 <em>same</em> build, not as a long-term storage format; keep the original <abbr title="JavaScript Object Notation">JSON</abbr> text if a saved document needs to be readable by a future library version.</p> </div> <p>The idea of a document you can read without parsing comes from zero-copy formats such as <a href="https://github.com/google/flatbuffers">FlatBuffers</a> and <a href="https://github.com/yandex/yaff">YaFF</a>; the check <a href=../../api/basic_json_document/load/ ><code>load()</code></a> runs follows the idea of FlatBuffers' Verifier. No code is taken from either.</p> <h2 id=choosing-between-json-ordered_json-the-sax-interface-and-json_view>Choosing between <code>json</code>, <code>ordered_json</code>, the <abbr title="Simple API for XML">SAX</abbr> interface, and <code>json_view</code><a class=headerlink href=#choosing-between-json-ordered_json-the-sax-interface-and-json_view title="Permanent link">¶</a></h2> <table> <thead> <tr> <th></th> <th><a href=../../api/json/ ><code>json</code></a> / <a href=../../api/ordered_json/ ><code>ordered_json</code></a></th> <th><a href=../parsing/sax_interface/ ><abbr title="Simple API for XML">SAX</abbr> interface</a></th> <th><a href=../../api/json_document/ ><code>json_document</code></a> / <a href=../../api/json_view/ ><code>json_view</code></a></th> <th><a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> / <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a></th> </tr> </thead> <tbody> <tr> <td><strong>Ownership</strong></td> <td>owns every value</td> <td>owns nothing; you decide what to keep, in your handler</td> <td>borrows or owns the <em>text</em>; the index is always owned by the document</td> <td>same as <code>json_document</code>; edits go to storage the document owns</td> </tr> <tr> <td><strong>Mutability</strong></td> <td>freely mutable</td> <td>not applicable (a one-shot event stream)</td> <td>read-only</td> <td><a href=../../api/basic_json_document/set/ ><code>set</code></a>/<a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a>/<a href=../../api/basic_json_document/insert/ ><code>insert</code></a>/<a href=../../api/basic_json_document/erase/ ><code>erase</code></a> edit in place; the source text is never rewritten</td> </tr> <tr> <td><strong>What you get</strong></td> <td>a full tree you can read, write, and keep as long as you like</td> <td>a sequence of callbacks; whatever your handler builds from them</td> <td>a flat index plus, on demand, <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d <code>json</code>/<code>ordered_json</code> values for the parts you actually use</td> <td>the same, plus <a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document that keeps the member order and, with <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a>, the spelling of every untouched number</td> </tr> <tr> <td><strong>Typical use</strong></td> <td>general-purpose <abbr title="JavaScript Object Notation">JSON</abbr> handling: config, request/response bodies you build or modify, anything you hold onto</td> <td>validating or projecting a text into your own data structure without ever holding the whole thing as <abbr title="JavaScript Object Notation">JSON</abbr></td> <td>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</td> <td>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</td> </tr> <tr> <td><strong>Caching/reload</strong></td> <td>not applicable — re-parse, or roll your own serialization</td> <td>not applicable</td> <td><a href=../../api/basic_json_document/save/ ><code>save()</code></a>/<a href=../../api/basic_json_document/load/ ><code>load()</code></a>: cache the parsed index as an image and reload it without parsing</td> <td>same, saving the document's current — possibly edited — state</td> </tr> </tbody> </table> <p class=nj-table-caption>Each column is one way to work with <abbr title="JavaScript Object Notation">JSON</abbr> in this library, and each row compares one property of them.</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li><a href=../../api/basic_json_document/ ><code>basic_json_document</code></a> - the document that owns the parsLine truncated
|
||||
</code></pre></div> </details> <p><a href=../../api/ordered_json_document/ ><code>ordered_json_document</code></a> is the equivalent for <a href=../../api/ordered_json/ ><code>ordered_json</code></a>, just as <a href=../../api/ordered_json/ ><code>ordered_json</code></a> is to <a href=../../api/json/ ><code>json</code></a>.</p> <p>A view behaves like a <code>basic_json</code> 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 <code>operator[]</code> are the most visible differences. <a href=differences/ >Differences from <code>basic_json</code></a> lists all of them.</p> <h2 id=where-to-go-next>Where to go next<a class=headerlink href=#where-to-go-next title="Permanent link">¶</a></h2> <ul> <li><a href=lifetime/ >Ownership and lifetime</a> - when a document borrows its source text, and how long a view stays valid</li> <li><a href=differences/ >Differences from <code>basic_json</code></a> - everything a view does differently from a parsed <code>basic_json</code> value</li> <li><a href=reading/ >Reading values</a> - converting with <code>get<T>()</code>, reading strings and numbers without copying, and writing a view back with <code>dump()</code></li> <li><a href=generic_code/ >Writing generic code</a> - functions and <code>from_json()</code> overloads that accept views</li> <li><a href=editing/ >Editing a document</a> - patching a document in place, keeping its member order and number spellings</li> <li><a href=images/ >Images</a> - saving a parsed document and loading it back without parsing</li> </ul> <h2 id=choosing-between-json-ordered_json-the-sax-interface-and-json_view>Choosing between <code>json</code>, <code>ordered_json</code>, the <abbr title="Simple API for XML">SAX</abbr> interface, and <code>json_view</code><a class=headerlink href=#choosing-between-json-ordered_json-the-sax-interface-and-json_view title="Permanent link">¶</a></h2> <table> <thead> <tr> <th></th> <th><a href=../../api/json/ ><code>json</code></a> / <a href=../../api/ordered_json/ ><code>ordered_json</code></a></th> <th><a href=../parsing/sax_interface/ ><abbr title="Simple API for XML">SAX</abbr> interface</a></th> <th><a href=../../api/json_document/ ><code>json_document</code></a> / <a href=../../api/json_view/ ><code>json_view</code></a></th> <th><a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> / <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a></th> </tr> </thead> <tbody> <tr> <td><strong>Ownership</strong></td> <td>owns every value</td> <td>owns nothing; you decide what to keep, in your handler</td> <td>borrows or owns the <em>text</em>; the index is always owned by the document</td> <td>same as <code>json_document</code>; edits go to storage the document owns</td> </tr> <tr> <td><strong>Mutability</strong></td> <td>freely mutable</td> <td>not applicable (a one-shot event stream)</td> <td>read-only</td> <td><a href=../../api/basic_json_document/set/ ><code>set</code></a>/<a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a>/<a href=../../api/basic_json_document/insert/ ><code>insert</code></a>/<a href=../../api/basic_json_document/erase/ ><code>erase</code></a> edit in place; the source text is never rewritten</td> </tr> <tr> <td><strong>What you get</strong></td> <td>a full tree you can read, write, and keep as long as you like</td> <td>a sequence of callbacks; whatever your handler builds from them</td> <td>a flat index plus, on demand, <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d <code>json</code>/<code>ordered_json</code> values for the parts you actually use</td> <td>the same, plus <a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document that keeps the member order and, with <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a>, the spelling of every untouched number</td> </tr> <tr> <td><strong>Typical use</strong></td> <td>general-purpose <abbr title="JavaScript Object Notation">JSON</abbr> handling: config, request/response bodies you build or modify, anything you hold onto</td> <td>validating or projecting a text into your own data structure without ever holding the whole thing as <abbr title="JavaScript Object Notation">JSON</abbr></td> <td>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</td> <td>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</td> </tr> <tr> <td><strong>Caching/reload</strong></td> <td>not applicable — re-parse, or roll your own serialization</td> <td>not applicable</td> <td><a href=../../api/basic_json_document/save/ ><code>save()</code></a>/<a href=../../api/basic_json_docLine truncated
|
||||
+15
-395
@@ -50,406 +50,26 @@ true
|
||||
|
||||
[`ordered_json_document`](https://json.nlohmann.me/api/ordered_json_document/index.md) is the equivalent for [`ordered_json`](https://json.nlohmann.me/api/ordered_json/index.md), just as [`ordered_json`](https://json.nlohmann.me/api/ordered_json/index.md) is to [`json`](https://json.nlohmann.me/api/json/index.md).
|
||||
|
||||
## Ownership and lifetime
|
||||
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`](https://json.nlohmann.me/features/json_view/differences/index.md) lists all of them.
|
||||
|
||||
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call [`owns_source()`](https://json.nlohmann.me/api/basic_json_document/owns_source/index.md) to find out which happened. [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) decides this from the value category and type of its argument (an lvalue `std::string` is borrowed; an rvalue `std::string` is moved in, owned without a copy; a stream is read into an owned buffer; and so on — see [`parse`'s Notes](https://json.nlohmann.me/api/basic_json_document/parse/#notes) for the full table). [`parse_copy()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) always owns a copy, regardless of the input.
|
||||
## Where to go next
|
||||
|
||||
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 `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()`](https://json.nlohmann.me/api/basic_json_document/read/index.md) or [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) into it),
|
||||
- the document has not had [`shrink_to_fit()`](https://json.nlohmann.me/api/basic_json_document/shrink_to_fit/index.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()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) whenever any of the other conditions above was not met.
|
||||
|
||||
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 `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 `std::shared_ptr` in the document and a `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()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) is not callable on a temporary document: `auto v = json_document::parse(text).root();` does not compile. Give the document a name first.
|
||||
|
||||
Example: borrowed and owned documents, and when views become invalid
|
||||
|
||||
```
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// BORROWED: doc only points into `text`; `text` must outlive `doc`
|
||||
std::string text = R"({"a": 1})";
|
||||
json_document doc = json_document::parse(text);
|
||||
std::cout << doc.owns_source() << '\n'; // false
|
||||
|
||||
// a view is valid as long as the document is alive, has not been
|
||||
// re-parsed (read()) or shrunk (shrink_to_fit()), and -- if borrowed --
|
||||
// the source text is alive
|
||||
nlohmann::json_view v = doc.root();
|
||||
std::cout << v.is_object() << '\n';
|
||||
|
||||
// re-parsing the SAME document invalidates views taken before the call;
|
||||
// `v` above must not be used after this line
|
||||
doc.read(R"([1, 2, 3])");
|
||||
v = doc.root(); // take a fresh view instead
|
||||
std::cout << v.is_array() << '\n';
|
||||
|
||||
// moving the document does not invalidate views: the node index is
|
||||
// heap-allocated and does not move with the document object
|
||||
json_document moved = std::move(doc);
|
||||
std::cout << v.is_array() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
```
|
||||
|
||||
## What is the same as `parse()`
|
||||
|
||||
- **Accept/reject.** [`accept()`](https://json.nlohmann.me/api/basic_json_document/accept/index.md) and [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) accept and reject exactly the same inputs as [`basic_json::accept()`](https://json.nlohmann.me/api/basic_json/accept/index.md)/[`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.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. `allow_exceptions == false` skips the replay and gives a [discarded](https://json.nlohmann.me/api/basic_json_document/is_discarded/index.md) document instead of throwing, just as it gives a discarded value for `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 `basic_json::parse()`.
|
||||
|
||||
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](https://json.nlohmann.me/api/macros/json_strict_nul_handling/index.md) and [`JSON_NOEXCEPTION`](https://json.nlohmann.me/api/macros/json_noexception/index.md)/[`JSON_THROW_USER`](https://json.nlohmann.me/api/macros/json_throw_user/index.md) behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or how it reports errors. Two more macros add context to the exceptions of views, as they do for those of `basic_json`:
|
||||
|
||||
- With [`JSON_DIAGNOSTICS`](https://json.nlohmann.me/api/macros/json_diagnostics/index.md), an exception names the JSON Pointer path of the value within the document.
|
||||
- With [`JSON_DIAGNOSTIC_POSITIONS`](https://json.nlohmann.me/api/macros/json_diagnostic_positions/index.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`](https://json.nlohmann.me/home/exceptions/#jsonexceptioninvalid_iterator207)), because a view's iterator does not know the value it iterates over.
|
||||
|
||||
The other macros only affect the values `materialize()` produces, through `BasicJsonType`.
|
||||
|
||||
## What is different
|
||||
|
||||
- **Only 64-bit integers.** `basic_json_document<BasicJsonType>` requires `BasicJsonType::number_integer_t` and `number_unsigned_t` to both be 64 bits wide; this is a compile-time `static_assert`.
|
||||
- **A 4 GiB input limit.** An input of 4294967280 bytes (4 GiB minus 16 bytes) or more throws [`out_of_range.416`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range416), a limit `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 `std::istream`: the stream is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a `std::string` yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and was about 6 to 12 % faster than parsing from an `std::ifstream`.
|
||||
- **Fewer source positions after edits.** With [`JSON_DIAGNOSTIC_POSITIONS`](https://json.nlohmann.me/api/macros/json_diagnostic_positions/index.md) enabled, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) sets the same positions as `basic_json::parse()`. In an [editable document](https://json.nlohmann.me/api/json_editable_document/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/#notes).
|
||||
- **Objects iterate in document order.** [`begin()`](https://json.nlohmann.me/api/basic_json_view/begin/index.md)/ [`end()`](https://json.nlohmann.me/api/basic_json_view/end/index.md) and [`items()`](https://json.nlohmann.me/api/basic_json_view/items/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d value can print members in a different order than iterating the view they came from.
|
||||
- **Chained access is safe.** [`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md) with a missing key, an index out of range, or an unresolvable JSON pointer returns a [discarded](https://json.nlohmann.me/api/basic_json_view/is_discarded/index.md) view, and `operator[]` on a discarded view returns a discarded view without throwing: `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`](https://json.nlohmann.me/api/basic_json_view/at/index.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 `operator<`. [`operator==`](https://json.nlohmann.me/api/basic_json_view/operator_eq/index.md) and [`operator!=`](https://json.nlohmann.me/api/basic_json_view/operator_ne/index.md) are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) or [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would produce equal values for them, without ever building a tree to do it. For ordering, too, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) is the way to get a value you can compare.
|
||||
|
||||
## Duplicate keys
|
||||
|
||||
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[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md), [`at`](https://json.nlohmann.me/api/basic_json_view/at/index.md), [`find`](https://json.nlohmann.me/api/basic_json_view/find/index.md), [`value`](https://json.nlohmann.me/api/basic_json_view/value/index.md), [`contains`](https://json.nlohmann.me/api/basic_json_view/contains/index.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 `{"a":1,"a":2}`, `view["a"]` is `1` while `parse(text)["a"]` is `2`. [`count`](https://json.nlohmann.me/api/basic_json_view/count/index.md) counts every member with the key, so it is `2` here.
|
||||
|
||||
[`begin()`](https://json.nlohmann.me/api/basic_json_view/begin/index.md)/[`end()`](https://json.nlohmann.me/api/basic_json_view/end/index.md) and [`items()`](https://json.nlohmann.me/api/basic_json_view/items/index.md) visit *every* occurrence, in document order, and [`size()`](https://json.nlohmann.me/api/basic_json_view/size/index.md) counts all of them, so `view.size()` can be larger than `view.materialize().size()`.
|
||||
|
||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup to scan all members of the object, even when the key is found at the very first one: this made lookups in small objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first member of a key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return the first member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees duplicates is unpredictable, so neither choice is wrong.
|
||||
|
||||
The same holds for [`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.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 `get<point>()` of `{"x":1,"x":2}` reads `x == 1`, while `materialize().get<point>()` reads `2`.
|
||||
|
||||
**What stays the same as `parse()`?** [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) and [`get<std::map<...>>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) replay every member in order, so they keep the *last* value exactly like `basic_json::parse()` (at the position of the first occurrence of the key, for an [`ordered_json`](https://json.nlohmann.me/api/ordered_json/index.md)).
|
||||
|
||||
**How do I get the value `parse()` would give?** Either call `view.materialize()` and look the key up in the result, or iterate the members and keep the last match:
|
||||
|
||||
```
|
||||
// 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()`](https://json.nlohmann.me/api/basic_json_view/size/index.md) against the number of distinct keys, or reject duplicates when iterating.
|
||||
|
||||
## Getting values out without copying
|
||||
|
||||
[`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) converts most `T` directly from the flat index, without ever building a `basic_json` value for the conversion: `bool`, arithmetic types, `std::nullptr_t`, `std::string`/other `std::basic_string`s (copied once), `basic_json_view` itself, the standard containers and other types the library converts (`std::vector`, `std::map`, `std::list`, `std::array`, `std::pair`, `std::tuple`, `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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)`.get<T>()` instead: the subtree is built into a real `basic_json` value first, exactly as [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.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()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) (equivalently, `get<string_view_t>()`) returns a string as a `string_view_t` pointing into the document's [`source()`](https://json.nlohmann.me/api/basic_json_document/source/index.md) text — or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of allocating a new `std::string`.
|
||||
- [`number_token()`](https://json.nlohmann.me/api/basic_json_view/number_token/index.md) returns a number exactly as it was written in the source, e.g. `"1.50"`, `"1E2"`, or an integer with more digits than any number type holds, instead of rounding it into a `double`/`int64_t` the way `get<T>()` (and [`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.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 `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()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) does for an lvalue `std::string`, a `std::string_view`, or a C string), as long as that text is.
|
||||
|
||||
```
|
||||
struct record
|
||||
{
|
||||
std::string_view name;
|
||||
int count;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||
};
|
||||
|
||||
const std::string text = R"({"name": "widget", "count": 3})";
|
||||
const json_document doc = json_document::parse(text); // borrows text
|
||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||
```
|
||||
|
||||
Decoding into a `string_view` from a `json` value dangles at once
|
||||
|
||||
The same struct also compiles with `json::parse(text).get<record>()`, but there `name` points into the temporary `json` value, which is destroyed at the end of the statement. Use a named [`json_document`](https://json.nlohmann.me/api/json_document/index.md), as above, to decode such a struct.
|
||||
|
||||
## Writing generic code
|
||||
|
||||
A view's type names its `BasicJsonType` and its [`view_mode`](https://json.nlohmann.me/api/view_mode/index.md): [`json_view`](https://json.nlohmann.me/api/json_view/index.md) is `basic_json_view<json, view_mode::read_only>`, and [`json_editable_view`](https://json.nlohmann.me/api/json_editable_view/index.md) is `basic_json_view<json, view_mode::editable>`. These are different types. The only conversion between them turns a view of a read-only document into a view of an editable document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
// views of editable documents only
|
||||
template<typename BasicJsonType>
|
||||
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
|
||||
|
||||
// views of read-only documents only
|
||||
template<typename BasicJsonType>
|
||||
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
|
||||
|
||||
// views of either document type
|
||||
template<typename BasicJsonType, view_mode Mode>
|
||||
void f(basic_json_view<BasicJsonType, Mode> v);
|
||||
```
|
||||
|
||||
Views are passed by value: they are two pointers, cheap to copy, like a `std::string_view`. A `const&` works as well, but a non-`const` reference does not bind to the views that [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md), [`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md), and [`at()`](https://json.nlohmann.me/api/basic_json_view/at/index.md) return, so `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](https://json.nlohmann.me/api/basic_json_view/basic_json_view/index.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==`](https://json.nlohmann.me/api/basic_json_view/operator_eq/index.md).
|
||||
|
||||
[`is_json_view<T>`](https://json.nlohmann.me/api/is_json_view/index.md) tells views from other types, for constraints that cannot be written as a `basic_json_view<...>` parameter.
|
||||
|
||||
### A `from_json()` that reads views
|
||||
|
||||
[`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) on a view calls `T`'s `from_json()` with the view itself if that `from_json()` accepts views, and with [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) otherwise. The `from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/index.md), [`NLOHMANN_DEFINE_DERIVED_TYPE_*`](https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/index.md), and [`NLOHMANN_JSON_SERIALIZE_ENUM`](https://json.nlohmann.me/api/macros/nlohmann_json_serialize_enum/index.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 `materialize().get<T>()`.
|
||||
|
||||
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
|
||||
|
||||
```
|
||||
template < typename J, typename std::enable_if <
|
||||
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
|
||||
void from_json(const J& j, person& p)
|
||||
{
|
||||
p.name = j.at("name").template get<std::string>();
|
||||
p.age = j.at("age").template get<int>();
|
||||
}
|
||||
```
|
||||
|
||||
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`, `contains()`, `value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained `template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in [arbitrary type conversions](https://json.nlohmann.me/features/arbitrary_types/index.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); `materialize().get<T>()` would see the last one. Containers built from an object, such as `std::map`, keep the **last** value either way, as [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) does.
|
||||
|
||||
`to_json()` is unaffected: it writes `basic_json` values, never views.
|
||||
|
||||
## Writing a view back
|
||||
|
||||
[`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d value, see [Duplicate keys](#duplicate-keys). `materialize().dump()` gives a different result in both respects for a `json_view`.
|
||||
|
||||
By default, numbers are written the way [`basic_json::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would. [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) instead copies every number exactly as it was written in the source text — a price like `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 `double`/`int64_t` value.
|
||||
|
||||
[`operator<<`](https://json.nlohmann.me/api/basic_json_view/operator_ltlt/index.md) writes a view to a stream the way `basic_json`'s does, using the stream's `width`/`fill` for indentation.
|
||||
|
||||
## Editing a document
|
||||
|
||||
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](https://json.nlohmann.me/api/basic_json_document/index.md) — more conveniently spelled [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) or [`ordered_json_editable_document`](https://json.nlohmann.me/api/ordered_json_editable_document/index.md) — also lets you [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) a value, [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md) onto or [`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md) into an array, and [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md) an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
|
||||
|
||||
The [mode](https://json.nlohmann.me/api/view_mode/index.md) defaults to `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 `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 `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 `19.90` to `19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced it to a `double`/`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()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document writes members in document order — a member [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) added goes at the end, exactly where it was inserted, and an [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md)d member simply leaves a gap: everything around it keeps its place — and [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.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()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would write it, since there is no source spelling for a brand new value.
|
||||
|
||||
Example: patch a configuration, keeping member order and number spellings
|
||||
|
||||
```
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_editable_document;
|
||||
using nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "price": 19.90})";
|
||||
std::cout << text << "\n\n";
|
||||
|
||||
// patch two fields -- "price" is never touched
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
doc.set(doc.root(), "host", "db2");
|
||||
doc.set(doc.root(), "retries", 3);
|
||||
|
||||
// member order (the new member at the end) and the untouched number's
|
||||
// exact spelling survive
|
||||
std::cout << doc.root().dump(-1, ' ', false, json_editable_view::number_format::source) << '\n';
|
||||
|
||||
// the same patch on a plain json value: keys are sorted (object_t is a
|
||||
// std::map), and "price" is rewritten even though the patch never
|
||||
// touched it
|
||||
json plain = json::parse(text);
|
||||
plain["host"] = "db2";
|
||||
plain["retries"] = 3;
|
||||
std::cout << plain.dump() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}
|
||||
|
||||
{"name":"cache","host":"db2","port":6379,"price":19.90,"retries":3}
|
||||
{"host":"db2","name":"cache","port":6379,"price":19.9,"retries":3}
|
||||
```
|
||||
|
||||
### 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](https://json.nlohmann.me/api/basic_json_view/index.md) taken before an edit, including a previously obtained [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.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`](https://json.nlohmann.me/api/basic_json_document/push_back/index.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()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) is unaffected either way and stays valid across further edits. See [`basic_json_document`'s Edits](https://json.nlohmann.me/api/basic_json_document/#edits) for the details, and [`set`'s Exception safety](https://json.nlohmann.me/api/basic_json_document/set/#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](https://json.nlohmann.me/home/architecture/#node-index-of-json-views).
|
||||
|
||||
Step through the edits of a small document (the node fields follow the [architecture overview](https://json.nlohmann.me/home/architecture/#node-index-of-json-views)):
|
||||
|
||||
Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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()`](https://json.nlohmann.me/api/basic_json_document/save/index.md) writes a document as an *image*: a byte buffer that [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) still has to scan the whole input, [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/save/index.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()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/load/index.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()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) takes an [`image_check`](https://json.nlohmann.me/api/basic_json_document/load/#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()`](https://json.nlohmann.me/api/basic_json_document/save/index.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](https://json.nlohmann.me/api/basic_json_document/load/#notes) for exactly what each level does and does not guarantee.
|
||||
|
||||
Example: cache a parsed configuration as an image
|
||||
|
||||
```
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// a configuration a service parses once and then caches as an image, so
|
||||
// that later requests can load() it instead of parsing the text again
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]})";
|
||||
const json_document config = json_document::parse(text);
|
||||
|
||||
// save() turns the parsed document into a byte buffer: a 64-byte header,
|
||||
// the node index, the source text, and the decoded strings
|
||||
const std::vector<std::uint8_t> image = config.save();
|
||||
std::cout << image.size() << '\n';
|
||||
|
||||
// the same document always saves to the same bytes
|
||||
std::cout << (image == json_document::parse(text).save()) << '\n';
|
||||
|
||||
// loading the image back needs no parsing, yet dumps exactly what
|
||||
// parsing the text produced
|
||||
const json_document reloaded = json_document::load(image);
|
||||
std::cout << (reloaded.root().dump() == config.root().dump()) << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
316
|
||||
true
|
||||
true
|
||||
```
|
||||
|
||||
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()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from either.
|
||||
- [Ownership and lifetime](https://json.nlohmann.me/features/json_view/lifetime/index.md) - when a document borrows its source text, and how long a view stays valid
|
||||
- [Differences from `basic_json`](https://json.nlohmann.me/features/json_view/differences/index.md) - everything a view does differently from a parsed `basic_json` value
|
||||
- [Reading values](https://json.nlohmann.me/features/json_view/reading/index.md) - converting with `get<T>()`, reading strings and numbers without copying, and writing a view back with `dump()`
|
||||
- [Writing generic code](https://json.nlohmann.me/features/json_view/generic_code/index.md) - functions and `from_json()` overloads that accept views
|
||||
- [Editing a document](https://json.nlohmann.me/features/json_view/editing/index.md) - patching a document in place, keeping its member order and number spellings
|
||||
- [Images](https://json.nlohmann.me/features/json_view/images/index.md) - saving a parsed document and loading it back without parsing
|
||||
|
||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||
|
||||
| | [`json`](https://json.nlohmann.me/api/json/index.md) / [`ordered_json`](https://json.nlohmann.me/api/ordered_json/index.md) | [SAX interface](https://json.nlohmann.me/features/parsing/sax_interface/index.md) | [`json_document`](https://json.nlohmann.me/api/json_document/index.md) / [`json_view`](https://json.nlohmann.me/api/json_view/index.md) | [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) / [`json_editable_view`](https://json.nlohmann.me/api/json_editable_view/index.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`](https://json.nlohmann.me/api/basic_json_document/set/index.md)/[`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md)/[`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md)/[`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document that keeps the member order and, with [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.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()`](https://json.nlohmann.me/api/basic_json_document/save/index.md)/[`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current — possibly edited — state |
|
||||
| | [`json`](https://json.nlohmann.me/api/json/index.md) / [`ordered_json`](https://json.nlohmann.me/api/ordered_json/index.md) | [SAX interface](https://json.nlohmann.me/features/parsing/sax_interface/index.md) | [`json_document`](https://json.nlohmann.me/api/json_document/index.md) / [`json_view`](https://json.nlohmann.me/api/json_view/index.md) | [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) / [`json_editable_view`](https://json.nlohmann.me/api/json_editable_view/index.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`](https://json.nlohmann.me/api/basic_json_document/set/index.md)/[`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md)/[`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md)/[`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document that keeps the member order and, with [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.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()`](https://json.nlohmann.me/api/basic_json_document/save/index.md)/[`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md): cache the parsed index as an [image](https://json.nlohmann.me/features/json_view/images/index.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.
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
# Ownership and lifetime
|
||||
|
||||
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call
|
||||
[`owns_source()`](../../api/basic_json_document/owns_source.md) to find out which happened.
|
||||
[`parse()`](../../api/basic_json_document/parse.md) decides this from the value category and type of its argument (an
|
||||
lvalue `#!cpp std::string` is borrowed; an rvalue `#!cpp std::string` is moved in, owned without a copy; a stream is
|
||||
read into an owned buffer; and so on — see [`parse`'s Notes](../../api/basic_json_document/parse.md#notes) for the
|
||||
full table). [`parse_copy()`](../../api/basic_json_document/parse_copy.md) always owns a copy, regardless of the input.
|
||||
|
||||
!!! warning "A borrowed document depends on your buffer"
|
||||
|
||||
If a document borrows its text, that text **must outlive the document** (and every view taken from it) and must
|
||||
stay **unmodified** for as long as the document exists, since floating-point values and unescaped strings are
|
||||
read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior,
|
||||
exactly as it would be for a dangling `#!cpp std::string_view`.
|
||||
|
||||
## When a view is valid
|
||||
|
||||
A view is valid only while all of the following hold:
|
||||
|
||||
- the document is alive,
|
||||
- the document has not been re-parsed since the view was taken (with
|
||||
[`read()`](../../api/basic_json_document/read.md) or [`parse()`](../../api/basic_json_document/parse.md) into it),
|
||||
- the document has not had [`shrink_to_fit()`](../../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
||||
- if the document borrows its source text, that text is still alive and unmodified.
|
||||
|
||||
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
||||
allocation that keeps its address across the move. Take a fresh view from
|
||||
[`root()`](../../api/basic_json_document/root.md) whenever any of the other conditions above was not met. Edits of an
|
||||
[editable document](editing.md) keep views valid, too; see [What stays valid](editing.md#what-stays-valid-and-what-an-edit-costs).
|
||||
|
||||
<div class="nj-fig-wrap" markdown>
|
||||
<svg class="nj-fig nj-jsonview " viewBox="0 0 840 572" role="img" aria-labelledby="fig-jvt-title fig-jvt-title-d" xmlns="http://www.w3.org/2000/svg">
|
||||
<title id="fig-jvt-title">When a json_view becomes invalid</title>
|
||||
<desc id="fig-jvt-title-d">Four timelines. First: a document borrows a std::string; when the string is destroyed, the document and every view taken from it dangle. Second: doc.read() re-parses the document; views taken before are invalid, a fresh view from root() is valid. Third: doc.shrink_to_fit() has the same effect. Fourth: moving the document does not invalidate its views; destroying the moved-to document does.</desc>
|
||||
<defs>
|
||||
<marker id="jvt-bad" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker>
|
||||
</defs>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="210" y="12" width="34" height="12" rx="3"/>
|
||||
<text class="nj-jsonview-note" x="252" y="23" xml:space="preserve">alive / valid</text>
|
||||
<rect class="nj-jsonview-bad" x="370" y="12" width="34" height="12" rx="3"/>
|
||||
<text class="nj-jsonview-note" x="412" y="23" xml:space="preserve">dangling: using it is undefined behavior</text>
|
||||
<path class="nj-jsonview-arr nj-jsonview-c-line" d="M210 44 L820 44"/>
|
||||
<text class="nj-jsonview-tiny" x="820" y="38" text-anchor="end" xml:space="preserve">time →</text>
|
||||
<text class="nj-jsonview-h nj-jsonview-sm2" x="16" y="74" xml:space="preserve">The borrowed text is destroyed</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="99" text-anchor="end" xml:space="preserve">std::string text</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="210" y="87" width="260" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="127" text-anchor="end" xml:space="preserve">json_document doc</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="270" y="115" width="200" height="16" rx="3"/>
|
||||
<rect class="nj-jsonview-bad" x="470" y="115" width="350" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="155" text-anchor="end" xml:space="preserve">json_view v</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="330" y="143" width="140" height="16" rx="3"/>
|
||||
<rect class="nj-jsonview-bad" x="470" y="143" width="350" height="16" rx="3"/>
|
||||
<path class="nj-jsonview-evt" d="M470 82 V168"/>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="474" y="80" xml:space="preserve">text destroyed</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-onbad" x="476" y="155" xml:space="preserve">every view, and the document itself</text>
|
||||
<text class="nj-jsonview-h nj-jsonview-sm2" x="16" y="202" xml:space="preserve">The document is re-parsed</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="227" text-anchor="end" xml:space="preserve">json_document doc</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="210" y="215" width="610" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="255" text-anchor="end" xml:space="preserve">json_view v</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="270" y="243" width="200" height="16" rx="3"/>
|
||||
<rect class="nj-jsonview-bad" x="470" y="243" width="350" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="283" text-anchor="end" xml:space="preserve">v = doc.root()</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="530" y="271" width="290" height="16" rx="3"/>
|
||||
<path class="nj-jsonview-evt" d="M470 210 V296"/>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="474" y="208" xml:space="preserve">doc.read(other)</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-onbad" x="476" y="255" xml:space="preserve">views taken before</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-onok" x="536" y="283" xml:space="preserve">a fresh view is valid again</text>
|
||||
<text class="nj-jsonview-h nj-jsonview-sm2" x="16" y="330" xml:space="preserve">The document is shrunk</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="355" text-anchor="end" xml:space="preserve">json_document doc</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="210" y="343" width="610" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="383" text-anchor="end" xml:space="preserve">json_view v</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="270" y="371" width="200" height="16" rx="3"/>
|
||||
<rect class="nj-jsonview-bad" x="470" y="371" width="350" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="411" text-anchor="end" xml:space="preserve">v = doc.root()</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="530" y="399" width="290" height="16" rx="3"/>
|
||||
<path class="nj-jsonview-evt" d="M470 338 V424"/>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="474" y="336" xml:space="preserve">doc.shrink_to_fit()</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-onbad" x="476" y="383" xml:space="preserve">views taken before</text>
|
||||
<text class="nj-jsonview-note nj-jsonview-onok" x="536" y="411" xml:space="preserve">a fresh view is valid again</text>
|
||||
<text class="nj-jsonview-h nj-jsonview-sm2" x="16" y="458" xml:space="preserve">The document is moved, then destroyed</text>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="483" text-anchor="end" xml:space="preserve">json_document doc</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="210" y="471" width="210" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="511" text-anchor="end" xml:space="preserve">moved = std::move(doc)</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="420" y="499" width="260" height="16" rx="3"/>
|
||||
<text class="nj-jsonview-mono nj-jsonview-sm" x="198" y="539" text-anchor="end" xml:space="preserve">json_view v</text>
|
||||
<rect class="nj-jsonview-bar nj-jsonview-c-struct" x="270" y="527" width="410" height="16" rx="3"/>
|
||||
<rect class="nj-jsonview-bad" x="680" y="527" width="140" height="16" rx="3"/>
|
||||
<path class="nj-jsonview-evt nj-jsonview-evt--ok" d="M420 466 V552"/>
|
||||
<text class="nj-jsonview-tiny" x="424" y="464" xml:space="preserve">std::move: views stay valid</text>
|
||||
<path class="nj-jsonview-evt" d="M680 466 V552"/>
|
||||
<text class="nj-jsonview-tiny nj-jsonview-c-bad" x="684" y="464" xml:space="preserve">moved is destroyed</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="nj-caption" markdown>Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.</p>
|
||||
|
||||
## Why views can dangle
|
||||
|
||||
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
|
||||
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
|
||||
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
|
||||
and a `lock()` to every access — exactly the costs the view exists to avoid. What is caught is the obvious case:
|
||||
because a view dies with its document, [`root()`](../../api/basic_json_document/root.md) is not callable on a
|
||||
temporary document: `#!cpp auto v = json_document::parse(text).root();` does not compile. Give the document a name
|
||||
first.
|
||||
|
||||
The same holds for what a view hands out without copying: a string from
|
||||
[`get_string()`](../../api/basic_json_view/get_string.md), a token from
|
||||
[`number_token()`](../../api/basic_json_view/number_token.md), and [`string_view` members](reading.md#string_view-members)
|
||||
of decoded structs are valid only as long as the document and, if it borrows, its source text.
|
||||
|
||||
??? example "Example: borrowed and owned documents, and when views become invalid"
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// BORROWED: doc only points into `text`; `text` must outlive `doc`
|
||||
std::string text = R"({"a": 1})";
|
||||
json_document doc = json_document::parse(text);
|
||||
std::cout << doc.owns_source() << '\n'; // false
|
||||
|
||||
// a view is valid as long as the document is alive, has not been
|
||||
// re-parsed (read()) or shrunk (shrink_to_fit()), and -- if borrowed --
|
||||
// the source text is alive
|
||||
nlohmann::json_view v = doc.root();
|
||||
std::cout << v.is_object() << '\n';
|
||||
|
||||
// re-parsing the SAME document invalidates views taken before the call;
|
||||
// `v` above must not be used after this line
|
||||
doc.read(R"([1, 2, 3])");
|
||||
v = doc.root(); // take a fresh view instead
|
||||
std::cout << v.is_array() << '\n';
|
||||
|
||||
// moving the document does not invalidate views: the node index is
|
||||
// heap-allocated and does not move with the document object
|
||||
json_document moved = std::move(doc);
|
||||
std::cout << v.is_array() << '\n';
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/lifetime/" rel="canonical"><link href=../ rel=prev><link href=../differences/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Ownership and lifetime - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#ownership-and-lifetime class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Ownership and lifetime </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><paLine truncated
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><string></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><utility></span>
|
||||
<span class=cp>#include</span><span class=w> </span><span class=cpf><nlohmann/json_view.hpp></span>
|
||||
|
||||
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_document</span><span class=p>;</span>
|
||||
|
||||
<span class=kt>int</span><span class=w> </span><span class=nf>main</span><span class=p>()</span>
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>boolalpha</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// BORROWED: doc only points into `text`; `text` must outlive `doc`</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"a": 1}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>doc</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>owns_source</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span><span class=w> </span><span class=c1>// false</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// a view is valid as long as the document is alive, has not been</span>
|
||||
<span class=w> </span><span class=c1>// re-parsed (read()) or shrunk (shrink_to_fit()), and -- if borrowed --</span>
|
||||
<span class=w> </span><span class=c1>// the source text is alive</span>
|
||||
<span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_view</span><span class=w> </span><span class=n>v</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>();</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>v</span><span class=p>.</span><span class=n>is_object</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// re-parsing the SAME document invalidates views taken before the call;</span>
|
||||
<span class=w> </span><span class=c1>// `v` above must not be used after this line</span>
|
||||
<span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>read</span><span class=p>(</span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>[1, 2, 3]</span><span class=dl>)</span><span class=s>"</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>v</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>();</span><span class=w> </span><span class=c1>// take a fresh view instead</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>v</span><span class=p>.</span><span class=n>is_array</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
|
||||
<span class=w> </span><span class=c1>// moving the document does not invalidate views: the node index is</span>
|
||||
<span class=w> </span><span class=c1>// heap-allocated and does not move with the document object</span>
|
||||
<span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>moved</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>move</span><span class=p>(</span><span class=n>doc</span><span class=p>);</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=n>v</span><span class=p>.</span><span class=n>is_array</span><span class=p>()</span><span class=w> </span><span class=o><<</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
|
||||
<span class=p>}</span>
|
||||
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=kc>false</span>
|
||||
<span class=kc>true</span>
|
||||
<span class=kc>true</span>
|
||||
<span class=kc>true</span>
|
||||
</code></pre></div> </details> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li><a href=../ >Zero-copy <abbr title="JavaScript Object Notation">JSON</abbr> views</a> - overview</li> <li><a href=../../../api/basic_json_document/ ><code>basic_json_document</code></a> - the document that owns the parsed index</li> <li><a href=../../../api/basic_json_document/owns_source/ ><code>owns_source()</code></a> - whether a document borrows its source text</li> </ul> <h2 id=version-history>Version history<a class=headerlink href=#version-history title="Permanent link">¶</a></h2> <ul> <li>Added in version 3.13.0 <span class=unreleased-version title="Not part of a release yet; the latest release is 3.12.0.">unreleased</span>.</li> </ul> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date" title="November 9, 2026 07:12:00 UTC">November 9, 2026</span> </span> </aside> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <nav class="md-footer__inner md-grid" aria-label=Footer> <a href=../ class="md-footer__link md-footer__link--prev" aria-label="Previous: Zero-copy JSON views"> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </div> <div class=md-footer__title> <span class=md-footer__direction> Previous </span> <div class=md-ellipsis> Zero-copy JSON views </div> </div> </a> <a href=../differences/ class="md-footer__link md-footer__link--next" aria-label="Next: Differences from basic_json"> <div class=md-footer__title> <span class=md-footer__direction> Next </span> <div class=md-ellipsis> Differences from basic_json </div> </div> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg> </div> </a> </nav> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> <div class=md-copyright__highlight> Copyright © 2013-2026 Niels Lohmann </div> </div> <div class=md-social> <a href="https://github.com/nlohmann" target="_blank" rel="noopener" title="github.com" class="md-social__link"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9Line truncated
|
||||
@@ -0,0 +1,83 @@
|
||||
# Ownership and lifetime
|
||||
|
||||
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call [`owns_source()`](https://json.nlohmann.me/api/basic_json_document/owns_source/index.md) to find out which happened. [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) decides this from the value category and type of its argument (an lvalue `std::string` is borrowed; an rvalue `std::string` is moved in, owned without a copy; a stream is read into an owned buffer; and so on — see [`parse`'s Notes](https://json.nlohmann.me/api/basic_json_document/parse/#notes) for the full table). [`parse_copy()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) always owns a copy, regardless of the input.
|
||||
|
||||
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 `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()`](https://json.nlohmann.me/api/basic_json_document/read/index.md) or [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) into it),
|
||||
- the document has not had [`shrink_to_fit()`](https://json.nlohmann.me/api/basic_json_document/shrink_to_fit/index.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()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) whenever any of the other conditions above was not met. Edits of an [editable document](https://json.nlohmann.me/features/json_view/editing/index.md) keep views valid, too; see [What stays valid](https://json.nlohmann.me/features/json_view/editing/#what-stays-valid-and-what-an-edit-costs).
|
||||
|
||||
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 `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 `std::shared_ptr` in the document and a `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()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) is not callable on a temporary document: `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()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md), a token from [`number_token()`](https://json.nlohmann.me/api/basic_json_view/number_token/index.md), and [`string_view` members](https://json.nlohmann.me/features/json_view/reading/#string_view-members) of decoded structs are valid only as long as the document and, if it borrows, its source text.
|
||||
|
||||
Example: borrowed and owned documents, and when views become invalid
|
||||
|
||||
```
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// BORROWED: doc only points into `text`; `text` must outlive `doc`
|
||||
std::string text = R"({"a": 1})";
|
||||
json_document doc = json_document::parse(text);
|
||||
std::cout << doc.owns_source() << '\n'; // false
|
||||
|
||||
// a view is valid as long as the document is alive, has not been
|
||||
// re-parsed (read()) or shrunk (shrink_to_fit()), and -- if borrowed --
|
||||
// the source text is alive
|
||||
nlohmann::json_view v = doc.root();
|
||||
std::cout << v.is_object() << '\n';
|
||||
|
||||
// re-parsing the SAME document invalidates views taken before the call;
|
||||
// `v` above must not be used after this line
|
||||
doc.read(R"([1, 2, 3])");
|
||||
v = doc.root(); // take a fresh view instead
|
||||
std::cout << v.is_array() << '\n';
|
||||
|
||||
// moving the document does not invalidate views: the node index is
|
||||
// heap-allocated and does not move with the document object
|
||||
json_document moved = std::move(doc);
|
||||
std::cout << v.is_array() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Zero-copy JSON views](https://json.nlohmann.me/features/json_view/index.md) - overview
|
||||
- [`basic_json_document`](https://json.nlohmann.me/api/basic_json_document/index.md) - the document that owns the parsed index
|
||||
- [`owns_source()`](https://json.nlohmann.me/api/basic_json_document/owns_source/index.md) - whether a document borrows its source text
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0 unreleased.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Reading values
|
||||
|
||||
## Getting values out without copying
|
||||
|
||||
[`get<T>()`](../../api/basic_json_view/get.md) converts most `T` directly from the flat index, without ever building a
|
||||
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
|
||||
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json_view` itself, the standard containers
|
||||
and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::list`, `#!cpp std::array`,
|
||||
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, ...), and the types of the `NLOHMANN_DEFINE_TYPE_*` and
|
||||
`NLOHMANN_JSON_SERIALIZE_ENUM` macros. Types whose `from_json()` is written for `basic_json` only go through
|
||||
[`materialize()`](../../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
|
||||
`basic_json` value first, exactly as [`parse()`](../../api/basic_json/parse.md) would, and converted from there. See
|
||||
[A `from_json()` that reads views](generic_code.md#a-from_json-that-reads-views) for how to write a `from_json()` that
|
||||
reads views, and [Duplicate keys](differences.md#duplicate-keys) for the one case in which the two ways give different
|
||||
results.
|
||||
|
||||
Two conversions never copy at all:
|
||||
|
||||
- [`get_string()`](../../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
||||
string as a `string_view_t` pointing into the document's [`source()`](../../api/basic_json_document/source.md) text
|
||||
— or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of
|
||||
allocating a new `#!cpp std::string`.
|
||||
- [`number_token()`](../../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
||||
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
||||
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
||||
[`basic_json::parse()`](../../api/basic_json/parse.md)) would.
|
||||
|
||||
Both results are only valid as long as the document and, if it [borrows its source text](lifetime.md), that text.
|
||||
|
||||
### `string_view` members
|
||||
|
||||
A struct whose member is a `#!cpp std::string_view` (C++17) can be decoded from a view with the
|
||||
`NLOHMANN_DEFINE_TYPE_*` macros, and the member is not copied either: for a string without escape sequences it points
|
||||
into the document's source text, and for a string with escape sequences into the document's buffer of decoded
|
||||
strings. The member is valid as long as the document is alive and, for a document that
|
||||
[borrows its source text](lifetime.md) (as [`parse()`](../../api/basic_json_document/parse.md) does for an lvalue
|
||||
`#!cpp std::string`, a `#!cpp std::string_view`, or a C string), as long as that text is.
|
||||
|
||||
```cpp
|
||||
struct record
|
||||
{
|
||||
std::string_view name;
|
||||
int count;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||
};
|
||||
|
||||
const std::string text = R"({"name": "widget", "count": 3})";
|
||||
const json_document doc = json_document::parse(text); // borrows text
|
||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||
```
|
||||
|
||||
!!! warning "Decoding into a `string_view` from a `json` value dangles at once"
|
||||
|
||||
The same struct also compiles with `#!cpp json::parse(text).get<record>()`, but there `name` points into the
|
||||
temporary `json` value, which is destroyed at the end of the statement. Use a named
|
||||
[`json_document`](../../api/json_document.md), as above, to decode such a struct.
|
||||
|
||||
## Writing a view back
|
||||
|
||||
[`dump()`](../../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
|
||||
`basic_json` value. It writes object members in document order and every occurrence of a repeated key, so it can
|
||||
differ from `#!cpp materialize().dump()`; see [Serialization](differences.md#serialization).
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,11 @@
|
||||
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/reading/" rel="canonical"><link href=../differences/ rel=prev><link href=../generic_code/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>Reading values - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#reading-values class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Reading values </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3Line truncated
|
||||
<span class=p>{</span>
|
||||
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string_view</span><span class=w> </span><span class=n>name</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=kt>int</span><span class=w> </span><span class=n>count</span><span class=p>;</span>
|
||||
<span class=w> </span><span class=n>NLOHMANN_DEFINE_TYPE_INTRUSIVE</span><span class=p>(</span><span class=n>record</span><span class=p>,</span><span class=w> </span><span class=n>name</span><span class=p>,</span><span class=w> </span><span class=n>count</span><span class=p>)</span>
|
||||
<span class=p>};</span>
|
||||
|
||||
<span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=w> </span><span class=n>text</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=sa>R</span><span class=s>"</span><span class=dl>(</span><span class=s>{"name": "widget", "count": 3}</span><span class=dl>)</span><span class=s>"</span><span class=p>;</span>
|
||||
<span class=k>const</span><span class=w> </span><span class=n>json_document</span><span class=w> </span><span class=n>doc</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>);</span><span class=w> </span><span class=c1>// borrows text</span>
|
||||
<span class=k>const</span><span class=w> </span><span class=n>record</span><span class=w> </span><span class=n>r</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>doc</span><span class=p>.</span><span class=n>root</span><span class=p>().</span><span class=n>get</span><span class=o><</span><span class=n>record</span><span class=o>></span><span class=p>();</span><span class=w> </span><span class=c1>// r.name points into text, valid while text and doc are</span>
|
||||
</code></pre></div> <div class="admonition warning"> <p class=admonition-title>Decoding into a <code>string_view</code> from a <code>json</code> value dangles at once</p> <p>The same struct also compiles with <code class=highlight><span class=n>json</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>).</span><span class=n>get</span><span class=o><</span><span class=n>record</span><span class=o>></span><span class=p>()</span></code>, but there <code>name</code> points into the temporary <code>json</code> value, which is destroyed at the end of the statement. Use a named <a href=../../../api/json_document/ ><code>json_document</code></a>, as above, to decode such a struct.</p> </div> <h2 id=writing-a-view-back>Writing a view back<a class=headerlink href=#writing-a-view-back title="Permanent link">¶</a></h2> <p><a href=../../../api/basic_json_view/dump/ ><code>dump()</code></a> serializes a view directly from the flat index, without ever building a <code>basic_json</code> value. It writes object members in document order and every occurrence of a repeated key, so it can differ from <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>dump</span><span class=p>()</span></code>; see <a href=../differences/#serialization>Serialization</a>.</p> <p>By default, numbers are written the way <a href=../../../api/basic_json/dump/ ><code>basic_json::dump()</code></a> would. <a href=../../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> instead copies every number exactly as it was written in the source text — a price like <code class=highlight><span class=mf>19.90</span></code>, a long order or account ID with more digits than any number type holds, or a high-precision coordinate — something <code>basic_json</code> cannot do at all, since parsing already reduces a number to its parsed <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> value. Together with an <a href=../editing/ >editable document</a>, this lets you patch a file without reformatting the rest of it.</p> <p><a href=../../../api/basic_json_view/operator_ltlt/ ><code>operator<<</code></a> writes a view to a stream the way <code>basic_json</code>'s does, using the stream's <code>width</code>/<code>fill</code> for indentation.</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li><a href=../ >Zero-copy <abbr title="JavaScript Object Notation">JSON</abbr> views</a> - overview</li> <li><a href=../../../api/basic_json_view/get/ ><code>get</code></a> - convert a view</li> <li><a href=../../../api/basic_json_view/dump/ ><code>dump</code></a> - serialize a view</li> <li><a href=../generic_code/ >Writing generic code</a> - functions and <code>from_json()</code> overloads that accept views</li> </ul> <h2 id=version-history>Version history<a class=headerlink href=#version-history title="Permanent link">¶</a></h2> <ul> <li>Added in version 3.13.0 <span class=unreleased-version title="Not part of a release yet; the latest release is 3.12.0.">unreleased</span>.</li> </ul> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date" title="November 9, 2026 07:12:00 UTC">November 9, 2026</span> </span> </aside> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <nav class="md-footer__inner md-grid" aria-label=Footer> <a href=../differences/ class="md-footer__link md-footer__link--prev" aria-label="Previous: Differences from basic_json"> <div class="md-footeLine truncated
|
||||
@@ -0,0 +1,52 @@
|
||||
# Reading values
|
||||
|
||||
## Getting values out without copying
|
||||
|
||||
[`get<T>()`](https://json.nlohmann.me/api/basic_json_view/get/index.md) converts most `T` directly from the flat index, without ever building a `basic_json` value for the conversion: `bool`, arithmetic types, `std::nullptr_t`, `std::string`/other `std::basic_string`s (copied once), `basic_json_view` itself, the standard containers and other types the library converts (`std::vector`, `std::map`, `std::list`, `std::array`, `std::pair`, `std::tuple`, `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()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)`.get<T>()` instead: the subtree is built into a real `basic_json` value first, exactly as [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would, and converted from there. See [A `from_json()` that reads views](https://json.nlohmann.me/features/json_view/generic_code/#a-from_json-that-reads-views) for how to write a `from_json()` that reads views, and [Duplicate keys](https://json.nlohmann.me/features/json_view/differences/#duplicate-keys) for the one case in which the two ways give different results.
|
||||
|
||||
Two conversions never copy at all:
|
||||
|
||||
- [`get_string()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) (equivalently, `get<string_view_t>()`) returns a string as a `string_view_t` pointing into the document's [`source()`](https://json.nlohmann.me/api/basic_json_document/source/index.md) text — or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of allocating a new `std::string`.
|
||||
- [`number_token()`](https://json.nlohmann.me/api/basic_json_view/number_token/index.md) returns a number exactly as it was written in the source, e.g. `"1.50"`, `"1E2"`, or an integer with more digits than any number type holds, instead of rounding it into a `double`/`int64_t` the way `get<T>()` (and [`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md)) would.
|
||||
|
||||
Both results are only valid as long as the document and, if it [borrows its source text](https://json.nlohmann.me/features/json_view/lifetime/index.md), that text.
|
||||
|
||||
### `string_view` members
|
||||
|
||||
A struct whose member is a `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](https://json.nlohmann.me/features/json_view/lifetime/index.md) (as [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) does for an lvalue `std::string`, a `std::string_view`, or a C string), as long as that text is.
|
||||
|
||||
```
|
||||
struct record
|
||||
{
|
||||
std::string_view name;
|
||||
int count;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||
};
|
||||
|
||||
const std::string text = R"({"name": "widget", "count": 3})";
|
||||
const json_document doc = json_document::parse(text); // borrows text
|
||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||
```
|
||||
|
||||
Decoding into a `string_view` from a `json` value dangles at once
|
||||
|
||||
The same struct also compiles with `json::parse(text).get<record>()`, but there `name` points into the temporary `json` value, which is destroyed at the end of the statement. Use a named [`json_document`](https://json.nlohmann.me/api/json_document/index.md), as above, to decode such a struct.
|
||||
|
||||
## Writing a view back
|
||||
|
||||
[`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) serializes a view directly from the flat index, without ever building a `basic_json` value. It writes object members in document order and every occurrence of a repeated key, so it can differ from `materialize().dump()`; see [Serialization](https://json.nlohmann.me/features/json_view/differences/#serialization).
|
||||
|
||||
By default, numbers are written the way [`basic_json::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would. [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) instead copies every number exactly as it was written in the source text — a price like `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 `double`/`int64_t` value. Together with an [editable document](https://json.nlohmann.me/features/json_view/editing/index.md), this lets you patch a file without reformatting the rest of it.
|
||||
|
||||
[`operator<<`](https://json.nlohmann.me/api/basic_json_view/operator_ltlt/index.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](https://json.nlohmann.me/features/json_view/index.md) - overview
|
||||
- [`get`](https://json.nlohmann.me/api/basic_json_view/get/index.md) - convert a view
|
||||
- [`dump`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) - serialize a view
|
||||
- [Writing generic code](https://json.nlohmann.me/features/json_view/generic_code/index.md) - functions and `from_json()` overloads that accept views
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0 unreleased.
|
||||
Reference in new issue
Block a user