mirror of
https://github.com/nlohmann/json.git
synced 2026-10-16 11:17:13 +00:00
Apply the review's wording fixes to the json_view documentation
- Copying is disabled because of its cost, not because of two documents borrowing the same buffer; the unique_ptr follows from move-only. - Inputs: streams, FILE*, and wide characters (strings, arrays, pointers) are all supported and owned; point to parse_copy() for owned copies. - The error replay only runs for failing input, builds no values, and is skipped with allow_exceptions = false. - read() writes copied inputs into the previous owned copy. - Views can dangle like std::string_view, and why there is no shared/weak pointer design. - The 4 GiB limit (16-byte nodes, 16-byte scanner steps), streams being read to the end first, which macros apply, why materialize() has no source positions, and wording nits (accept() vs basic_json::accept(), is_binary, is_discarded, see-also lists, size table, "back into"). Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
15 files changed
+84
-55
No files matched your search
@@ -12,6 +12,11 @@ Checks whether the input is valid JSON, accepting and rejecting exactly what
|
||||
function never throws an exception for invalid input, and the returned `#!cpp bool` is the only result -- no document
|
||||
is returned.
|
||||
|
||||
The result is the same as that of `BasicJsonType::accept()` by design; the difference is how the check is made. This
|
||||
function runs the document's index builder (and discards the index), while `BasicJsonType::accept()` runs the SAX
|
||||
parser with a handler that ignores every event. Use this one if you go on to parse the same input with
|
||||
[`parse()`](parse.md) anyway, so that both use the same code; either is fine for a plain yes/no answer.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`InputType`
|
||||
|
||||
@@ -16,8 +16,8 @@ basic_json_document(const basic_json_document&) = delete;
|
||||
2. Move constructor. Takes over `other`'s index and, if owned, its text; `other` is left as an empty document. Views
|
||||
taken from `other` before the move remain valid, because the index is heap-allocated independently of the
|
||||
`basic_json_document` object.
|
||||
3. `basic_json_document` is move-only. Copying is disabled because it would either duplicate a potentially large index
|
||||
and text, or leave two documents claiming to borrow the same buffer.
|
||||
3. `basic_json_document` is move-only. Copying is disabled because of its cost: a copy would duplicate the whole
|
||||
index, the decoded-string buffer, and the owned text, while a move is enough in every use we know of.
|
||||
|
||||
## Parameters
|
||||
|
||||
@@ -30,7 +30,7 @@ No-throw guarantee: the default and move constructors never throw exceptions.
|
||||
|
||||
## Complexity
|
||||
|
||||
Constant, for the default and move constructors.
|
||||
Constant.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@ A parsed JSON text, held as a flat index of its values
|
||||
([16 bytes per value](../../home/architecture.md#node-index-of-json-views)) instead of a tree of `BasicJsonType` values.
|
||||
Strings and numbers stay in the source text; only strings that contain escapes are decoded, into one buffer owned by
|
||||
the document. [`basic_json_view`](../basic_json_view/index.md) is a read-only handle to one value of a
|
||||
`basic_json_document`; [`materialize()`](../basic_json_view/materialize.md) turns a subtree back into the
|
||||
`basic_json_document`; [`materialize()`](../basic_json_view/materialize.md) turns a subtree into the
|
||||
`BasicJsonType` value that [`BasicJsonType::parse()`](../basic_json/parse.md) would have produced for it.
|
||||
|
||||
A document may **borrow** the text it was parsed from (the caller's buffer must then outlive the document) or **own**
|
||||
it (a copy, or an rvalue `#!cpp std::string` that was moved in); see [`owns_source`](owns_source.md). `basic_json_document`
|
||||
is move-only: copying a document would either duplicate a potentially large index and text, or leave two documents
|
||||
claiming to borrow the same buffer, so it is disabled.
|
||||
is move-only: a copy would duplicate the whole index, the decoded-string buffer, and the owned text, and a move is
|
||||
enough in every use we know of. The index, the decoded strings, and the owned text live in one heap block that the
|
||||
document holds through a `#!cpp std::unique_ptr`; this is also why views survive a move of the document.
|
||||
|
||||
## Template parameters
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
bool owns_source() const noexcept;
|
||||
```
|
||||
|
||||
Returns whether the document holds its own copy of the parsed text, as opposed to borrowing the caller's buffer.
|
||||
Returns whether the document holds its own copy of the parsed text or is borrowing the caller's buffer.
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -21,13 +21,15 @@ static basic_json_document parse(IteratorType first, IteratorType last,
|
||||
|
||||
Both overloads accept the same JSON text as [`BasicJsonType::parse()`](../basic_json/parse.md), with the same
|
||||
`ignore_comments`/`ignore_trailing_commas` options, but build a [`basic_json_document`](index.md) (a flat index into
|
||||
the input) instead of a tree of `BasicJsonType` values. The input must be byte-oriented (see the template parameters
|
||||
below): not every input type of `BasicJsonType::parse()` is supported.
|
||||
the input) instead of a tree of `BasicJsonType` values. They take the same inputs as `BasicJsonType::parse()`. Byte
|
||||
inputs that stay alive can be borrowed; all others (streams, `#!cpp FILE*`, wide characters, non-contiguous
|
||||
iterators) are read into a copy that the document owns -- see the Notes. Use [`parse_copy()`](parse_copy.md) to get an
|
||||
owned copy of a borrowable input as well.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`InputType`
|
||||
: A byte-oriented input, one of:
|
||||
: A compatible input, for instance:
|
||||
|
||||
- a `#!cpp std::string`, `#!cpp std::string_view`, or a C-style array of single-byte characters
|
||||
- a pointer to a null-terminated string of single-byte characters (`#!cpp char`, `#!cpp signed char`,
|
||||
@@ -35,12 +37,10 @@ below): not every input type of `BasicJsonType::parse()` is supported.
|
||||
- a container for which `#!cpp obj.data()` and `#!cpp obj.size()` give contiguous single-byte access, e.g.
|
||||
`#!cpp std::vector<char>` or `#!cpp std::vector<std::uint8_t>`
|
||||
- an `#!cpp std::istream` object
|
||||
- a wide string object (`#!cpp std::wstring`, `#!cpp std::u16string`, `#!cpp std::u32string`), which is converted
|
||||
to UTF-8
|
||||
|
||||
Other inputs are not supported: a `#!cpp FILE*`, and pointers to or arrays of wide characters (`#!cpp wchar_t`,
|
||||
`#!cpp char16_t`, `#!cpp char32_t`) are rejected at compile time by a `#!cpp static_assert`. (Use
|
||||
[`BasicJsonType::parse()`](../basic_json/parse.md) for these.)
|
||||
- a `#!cpp FILE*` pointer
|
||||
- wide characters (`#!cpp wchar_t`, `#!cpp char16_t`, `#!cpp char32_t`), which are converted to UTF-8: a wide
|
||||
string object (`#!cpp std::wstring`, `#!cpp std::u16string`, `#!cpp std::u32string`), an array (e.g. a wide
|
||||
string literal), or a pointer to a null-terminated wide string
|
||||
|
||||
`IteratorType`
|
||||
: an input iterator type, for instance a pair of pointers such as `ptr` and `ptr + len`, or a pair of
|
||||
@@ -78,7 +78,9 @@ discarded; see [`is_discarded`](is_discarded.md).
|
||||
|
||||
Throws the same exception [`BasicJsonType::parse()`](../basic_json/parse.md) throws for the same input and options --
|
||||
the same exception id, message, and position -- because on a failing input the library's own parser is run on the
|
||||
same bytes to produce the diagnostic. Additionally throws
|
||||
same bytes to produce the diagnostic. This replay runs only for a failing input, and costs about a lexing pass up to
|
||||
the error (its SAX handler builds no values); with `allow_exceptions` set to `#!cpp false`, it is skipped and a
|
||||
discarded document is returned instead. Additionally throws
|
||||
[`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416) if the input is 4294967280 bytes (4 GiB
|
||||
minus 16 bytes) or larger, a size [`BasicJsonType::parse()`](../basic_json/parse.md) does not reject.
|
||||
|
||||
@@ -95,7 +97,7 @@ Linear in the length of the input.
|
||||
| lvalue byte container (`std::string`, `std::vector<char>`, ...), `std::string_view`, C string, character array | **borrowed** -- `input` must outlive the document |
|
||||
| non-const rvalue `#!cpp std::string` | **owned**, moved in without a copy |
|
||||
| other rvalue byte container (including a `#!cpp const` rvalue `#!cpp std::string`) | **owned**, copied |
|
||||
| stream, wide string, or anything else read through the general input adapter | **owned**, read into a buffer (a stream is read to its end) |
|
||||
| stream, `#!cpp FILE*`, wide characters, or anything else read through the general input adapter | **owned**, read into a buffer (a stream is read to its end) |
|
||||
|
||||
For overload (2), a pair of pointers to single-byte integers (e.g. `#!cpp const char*`, `#!cpp std::uint8_t*`) is
|
||||
borrowed. From C++20 on, so is any other contiguous iterator over single bytes, such as
|
||||
|
||||
@@ -47,7 +47,9 @@ Every view taken from `#!cpp *this` before the call -- including the previous [`
|
||||
whether or not the new parse succeeds; take fresh views from [`root()`](root.md) afterward.
|
||||
|
||||
`input` is borrowed or owned by the same rules as [`parse()`](parse.md#notes); a document can borrow on one call and
|
||||
own on the next, since ownership is decided freshly each time.
|
||||
own on the next, since ownership is decided freshly each time. An input that is copied (see the table in
|
||||
[`parse()`](parse.md#notes)) is written into the document's previous owned copy, reusing its memory; a moved-in
|
||||
`#!cpp std::string` replaces it.
|
||||
|
||||
An integer argument that is not a `#!cpp bool` where the flags are expected, such as `#!cpp read(ptr, len)`, does not
|
||||
compile; see [`parse`](parse.md#notes).
|
||||
|
||||
@@ -39,7 +39,8 @@ auto v = doc.root();
|
||||
`root()` is a cheap handle into the document's index, not a copy of anything; call it as often as needed. The
|
||||
returned view is valid under the same conditions as any other view of the document -- see
|
||||
[Object inspection](../basic_json_view/index.md) -- in particular, it is invalidated by the next
|
||||
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document.
|
||||
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document. A view can dangle, like a
|
||||
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view.md#ownership-and-lifetime).
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ Constant.
|
||||
## See also
|
||||
|
||||
- [is_structured](is_structured.md) - return whether the type is structured
|
||||
- [size](size.md), [empty](empty.md) - the number of elements, and whether there are none
|
||||
- [size](size.md) - the number of elements
|
||||
- [empty](empty.md) - whether there are no elements
|
||||
- [`BasicJsonType::is_array`](../basic_json/is_array.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
bool is_binary() const noexcept;
|
||||
```
|
||||
|
||||
This function always returns `#!cpp false`: a JSON text has no binary values, so a view can never refer to one. The
|
||||
function exists for interface parity with [`BasicJsonType::is_binary`](../basic_json/is_binary.md).
|
||||
This function always returns `#!cpp false`: a document is only ever parsed from JSON text, never from CBOR or the other
|
||||
binary formats, and a JSON text has no binary values, so a view can never refer to one. The function exists for
|
||||
interface parity with [`BasicJsonType::is_binary`](../basic_json/is_binary.md).
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ that is itself [discarded](../basic_json_document/is_discarded.md) -- in particu
|
||||
|
||||
## Return value
|
||||
|
||||
`#!cpp true` if the view is discarded, `#!cpp false` otherwise.
|
||||
`#!cpp true` if the view or its document is discarded, `#!cpp false` otherwise.
|
||||
|
||||
## Exception safety
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ Constant.
|
||||
## See also
|
||||
|
||||
- [is_structured](is_structured.md) - return whether the type is structured
|
||||
- [size](size.md), [empty](empty.md) - the number of elements, and whether there are none
|
||||
- [size](size.md) - the number of elements
|
||||
- [empty](empty.md) - whether there are no elements
|
||||
- [`BasicJsonType::is_object`](../basic_json/is_object.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -37,7 +37,8 @@ Constant.
|
||||
|
||||
## See also
|
||||
|
||||
- [source_offset](source_offset.md) - byte offset of this value in the document's source text
|
||||
- [source_offset](source_offset.md) - byte offset of this value in the source text, which differs for strings: an
|
||||
escaped string has none
|
||||
- [`BasicJsonType::is_string`](../basic_json/is_string.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -14,8 +14,8 @@ if the view is [discarded](is_discarded.md).
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: if an exception is thrown, there are no changes to the view or the document it refers to (nothing
|
||||
about either is mutated by this function).
|
||||
Strong guarantee: there are no changes to the view or the document it refers to (nothing about either is mutated by
|
||||
this function).
|
||||
|
||||
## Exceptions
|
||||
|
||||
@@ -28,12 +28,13 @@ Linear in the size of the subtree.
|
||||
## Notes
|
||||
|
||||
`materialize()` replays the subtree through the same SAX builder [`BasicJsonType::parse()`](../basic_json/parse.md)
|
||||
uses internally, so the result matches it exactly -- including, for an object, that a repeated key keeps only its
|
||||
uses, so the result matches it exactly -- including, for an object, that a repeated key keeps only its
|
||||
last value. The replay is iterative, so it is not limited by the call stack the way a naive recursive conversion
|
||||
would be; the JSON nesting depth is limited only by available memory, as for `BasicJsonType::parse()` itself.
|
||||
|
||||
Unlike parsing with [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) enabled, the values
|
||||
produced by `materialize()` do not carry source positions: there is no lexer run during the replay to record them.
|
||||
produced by `materialize()` do not carry source positions. The index has the start offset of every value, but not the
|
||||
end of arrays, objects, and escaped strings, which the positions also need.
|
||||
|
||||
Calling `materialize()` on the same view repeatedly builds a new, independent `BasicJsonType` value each time; it
|
||||
never caches the result.
|
||||
|
||||
@@ -10,15 +10,15 @@ Returns the number of elements, as [`BasicJsonType::size()`](../basic_json/size.
|
||||
|
||||
The return value depends on the type and is defined as follows:
|
||||
|
||||
| Value type | return value |
|
||||
|----------------------|-----------------------------|
|
||||
| null | `0` |
|
||||
| discarded | `0` |
|
||||
| boolean | `1` |
|
||||
| string | `1` |
|
||||
| number | `1` |
|
||||
| object | number of key/value pairs |
|
||||
| array | number of elements |
|
||||
| Value type | return value |
|
||||
|------------|---------------------------|
|
||||
| null | `0` |
|
||||
| discarded | `0` |
|
||||
| boolean | `1` |
|
||||
| string | `1` |
|
||||
| number | `1` |
|
||||
| object | number of key/value pairs |
|
||||
| array | number of elements |
|
||||
|
||||
## Exception safety
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ most of the resulting tree is thrown away almost immediately.
|
||||
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
||||
options, but instead of a tree it builds a flat index of the values it found: one
|
||||
[16-byte entry](../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
||||
order. Strings and numbers are not copied out of the input; they stay in the source text, and are only decoded when
|
||||
actually needed (for a string, only if it contains escape sequences, into one shared buffer owned by the document).
|
||||
order. Strings and numbers are referenced in place in the source text; only strings that contain escape sequences
|
||||
are decoded, into one shared buffer owned by the document.
|
||||
|
||||
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
||||
index. It gives you the read-only, type-inspection part of the `basic_json` interface --
|
||||
@@ -31,7 +31,8 @@ index. It gives you the read-only, type-inspection part of the `basic_json` inte
|
||||
|
||||
Include `<nlohmann/json_view.hpp>` in addition to (or instead of) `<nlohmann/json.hpp>`. Parse into a
|
||||
[`json_document`](../api/json_document.md), inspect its [`root()`](../api/basic_json_document/root.md), and
|
||||
`materialize()` when you need a real value:
|
||||
`materialize()` when you need a real value. `materialize()` works on any view, not just the root, so you can turn just
|
||||
the subtree you need into a value:
|
||||
|
||||
??? example "Example: parse a document, inspect its root, and materialize it"
|
||||
|
||||
@@ -68,15 +69,19 @@ 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), and has not had
|
||||
[`shrink_to_fit()`](../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
||||
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.
|
||||
|
||||
Moving the 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)
|
||||
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.
|
||||
|
||||
Because a view dies with its document, [`root()`](../api/basic_json_document/root.md) is not callable on a temporary
|
||||
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
|
||||
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
|
||||
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
|
||||
and a `lock()` to every access -- exactly the costs the view exists to avoid. What is caught is the obvious case:
|
||||
because a view dies with its document, [`root()`](../api/basic_json_document/root.md) is not callable on a temporary
|
||||
document: `#!cpp auto v = json_document::parse(text).root();` does not compile. Give the document a name first.
|
||||
|
||||
??? example "Example: borrowed and owned documents, and when views become invalid"
|
||||
@@ -98,14 +103,19 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
|
||||
[`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.
|
||||
`#!cpp allow_exceptions == false` 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()`.
|
||||
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>`.
|
||||
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 others either do not apply ([`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md)
|
||||
needs parent pointers in `basic_json` values) or only affect the values `materialize()` produces, through
|
||||
`BasicJsonType`.
|
||||
|
||||
## What is different
|
||||
|
||||
@@ -113,12 +123,15 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
|
||||
`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.
|
||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`.
|
||||
`#!cpp basic_json::parse()` does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits
|
||||
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
||||
end must still fit.
|
||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
||||
is read to its end into a buffer the document owns, and parsing starts after that.
|
||||
- **No source positions on `materialize()`.** Even with
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) does not set them: there is no lexer run during the
|
||||
replay to record them.
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) does not set them. The index has the start offset of every
|
||||
value, but not the end of arrays, objects, and escaped strings, which the positions also need.
|
||||
- **Element access, iteration, `get<T>()`, JSON Pointer, `dump()`, and comparison are not (yet) provided** by
|
||||
`basic_json_view`. For now, [`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you
|
||||
can do those things with.
|
||||
|
||||
Reference in new issue
Block a user