docs: new design for the documentation site (#5934)

* docs: new design for the documentation site

Apply the brand to the Material for MkDocs site: Inter and JetBrains Mono
served by the site (upright and real italic), brand colours, blue header band
with the syntax pattern, wordmark in the header, new home page (hero, cards,
animated feature tour), 404 page, polish for tabs, search, footer, focus
rings, tables and code blocks, and theme features (code copy, previous/next
footer, TOC follow, search suggestions).

Draw the number railroad diagram, the parser callback events and the iterator
illustrations as inline SVGs that follow the light and dark scheme. The
generator scripts are in docs/mkdocs/scripts/figures; the feature slideshow
(json-light.svg, json-dark.svg, replacing json.gif on the home page) is built
by docs/mkdocs/scripts/slideshow. Decision nodes in five flowcharts become
hexagons, which need far less room.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: add figures, table captions and a macro chooser

- Figures with numbered captions: SAX stepper, json_view layout and edit
  stepper, exception map, JSON pointer and patch walkers, QA pipeline,
  scaling and binary size charts, roadmap lanes, release timeline,
  integration decision tree and namespace builder
- Table captions (nj-table-caption) on 9 pages
- Compile times: modules and PCH charts, filesystem section, re-measured
  Figure 1, links between features/modules.md and compile_times.md
- Interactive macro chooser for features/arbitrary_types.md
- Link check: exclude URLs that answer the plugin with 400/401/419 or are
  dead (customers.md)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: macro chooser generates adl_serializer for non-default-constructible types

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: cross-link concept pages and API pages in See also sections

Add See also links in both directions between the concept pages (features,
home, integration) and the API pages, write API links in code font, give every
entry a comment after a dash, and show the callback event figure on the parser
callbacks page.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: address review comments on the new design

- Home: add a "Latest release" button
- Sponsors: update the list and group it by tier
- Features: list all feature pages on the index page
- Releases: add highlights per release, link the 4.0 roadmap, add the first commit to the timeline
- Exceptions: prefix all example messages with the exception name
- API: no code font for format names in the See also links of the to_/from_ pages
- Merge Patch: show the document before, the patch, and the result
- Replace "--" with typographic dashes, mark external links with an icon, center Mermaid diagrams

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann authored and GitHub committed 2026-10-27 07:41:42 +01:00
1 parent 2bb2feeef4
commit a47170346a
417 files changed
+11100 -3041

No files matched your search

+45 -9
View File
@@ -124,26 +124,28 @@ For all the macros, the first parameter is the name of the class/struct. The `DE
```mermaid
flowchart TD
A["choosing a NLOHMANN_DEFINE_* macro"] --> B{"adding fields to a base class?"}
A["choosing a NLOHMANN_DEFINE_* macro"] --> B{{"adding fields to a base class?"}}
B -->|"yes"| C["...DERIVED_TYPE..."]
B -->|"no"| D["...TYPE..."]
C --> E{"need access to private members?"}
C --> E{{"need access to private members?"}}
D --> E
E -->|"yes"| F["...INTRUSIVE... (used inside the class)"]
E -->|"no"| G["...NON_INTRUSIVE... (used in the namespace)"]
F --> H{"only serializing, never parsing back?"}
F --> H{{"only serializing, never parsing back?"}}
G --> H
H -->|"yes"| I["...ONLY_SERIALIZE"]
H -->|"no"| J{"allow missing keys when parsing?"}
H -->|"no"| J{{"allow missing keys when parsing?"}}
J -->|"yes"| K["...WITH_DEFAULT"]
J -->|"no"| L["plain (missing keys throw)"]
I --> M{"need custom JSON key names?"}
I --> M{{"need custom JSON key names?"}}
K --> M
L --> M
M -->|"yes"| N["...WITH_NAMES"]
M -->|"no"| O["done"]
```
<p class="nj-caption" markdown>Decision tree for choosing a `NLOHMANN_DEFINE_*` macro: answer the questions from top to bottom; each answer picks one part of the macro name, and the parts are combined in this order. The table below lists the resulting macros.</p>
| Need access to private members | Need only serialization | Allow missing values when de-serializing | macro |
|------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| <div style="color: green;">:octicons-check-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | [**NLOHMANN_DEFINE_TYPE_INTRUSIVE**](../api/macros/nlohmann_define_type_intrusive.md) |
@@ -153,6 +155,8 @@ flowchart TD
| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_type_non_intrusive.md) |
| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | <div style="color: grey;">:octicons-skip-fill-24:</div> | [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_type_non_intrusive.md) |
<p class="nj-table-caption" markdown>Macros for classes and structs without a base class. A green check means yes, a red cross means no, and a grey skip sign means the column does not apply, as a macro that only serializes never reads values.</p>
For _derived_ classes and structs, use the following macros
| Need access to private members | Need only serialization | Allow missing values when de-serializing | macro |
@@ -164,12 +168,33 @@ For _derived_ classes and structs, use the following macros
| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_derived_type.md) |
| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | <div style="color: grey;">:octicons-skip-fill-24:</div> | [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_derived_type.md) |
<p class="nj-table-caption" markdown>Macros for derived classes and structs, read as Table 1.</p>
!!! info "Implementation limits"
- The current macro implementations are limited to at most 63 member variables. If you want to serialize/deserialize
types with more than 63 member variables, you need to define the `to_json`/`from_json` functions manually.
- For the `WITH_NAMES` variants the limit is halved to 31 member variables.
To pick the macro for your own type and get code to start from, answer the questions below.
<div class="nj-macro-chooser" data-variant="full" markdown>
<div class="nj-mc-fallback" markdown>
- Members are private: use an `INTRUSIVE` macro inside the class ([reference](../api/macros/nlohmann_define_type_intrusive.md){ data-nj-mc="intrusive" }).
- All members are public, or you cannot edit the class: use a `NON_INTRUSIVE` macro in the class's namespace ([reference](../api/macros/nlohmann_define_type_non_intrusive.md){ data-nj-mc="non_intrusive" }).
- The class adds members to a serializable base class: use a `DERIVED_TYPE` macro with the base class as second argument ([reference](../api/macros/nlohmann_define_derived_type.md){ data-nj-mc="derived" }).
- Missing keys should keep their default values: append `_WITH_DEFAULT`; otherwise a missing key throws.
- You only convert to JSON: append `_ONLY_SERIALIZE` instead.
- JSON keys differ from the member names: append `_WITH_NAMES` and write `"key", member` pairs ([reference](../api/macros/nlohmann_define_type_with_names.md){ data-nj-mc="with_names" }).
- Private members of a class you cannot edit: no macro works; write the conversion by hand ([third-party types](#how-do-i-convert-third-party-types){ data-nj-mc="third_party" }).
- The type is not default constructible and is converted in both directions: no macro works; specialize `adl_serializer` with a `from_json` that returns the value, or append `_ONLY_SERIALIZE` if you only convert to JSON ([non-default-constructible types](#how-can-i-use-get-for-non-default-constructiblenon-copyable-types){ data-nj-mc="non_default" }).
</div>
</div>
<p class="nj-caption" markdown>Macro chooser: each answer selects one part of the macro name; the generated program defines the type, calls the macro and converts a value to JSON and back. Combinations that no macro supports show the alternative instead of code.</p>
??? example "Example: using the `NLOHMANN_DEFINE_TYPE_*` macros"
The `to_json`/`from_json` functions for the `person` struct above can be created with:
@@ -250,7 +275,7 @@ For _derived_ classes and structs, use the following macros
The `NLOHMANN_DEFINE_TYPE_*`/`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros are generic over any `basic_json`
specialization, including `nlohmann::ordered_json`. Simply use `ordered_json` as the target type and members
are serialized in declaration order -- no separate macro or extra code is needed.
are serialized in declaration order — no separate macro or extra code is needed.
```cpp
namespace ns {
@@ -283,7 +308,7 @@ For _derived_ classes and structs, use the following macros
There is currently no `NLOHMANN_DEFINE_TYPE_*`-style macro for types that are not
[DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). This is not an
intentional omission of documentation -- no such macro exists yet; see
intentional omission of documentation — no such macro exists yet; see
[How can I use `get()` for non-default constructible/non-copyable types?](#how-can-i-use-get-for-non-default-constructiblenon-copyable-types)
for the manual pattern to use instead.
@@ -299,6 +324,8 @@ flowchart LR
B -->|"user specialization replaces the default"| E["user's adl_serializer specialization for T"]
```
<p class="nj-caption" markdown>How a conversion between `json` and a type `T` is dispatched: the default `adl_serializer` calls the free `to_json`/`from_json` functions found by argument-dependent lookup, while a user specialization of `adl_serializer` replaces this path.</p>
The library uses **JSON Serializers** to convert types to JSON.
The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)).
@@ -438,7 +465,7 @@ For smart pointers, class hierarchies, `std::variant`, and `std::chrono`, see
## Why can't I convert to/from `std::any`?
`std::any` is intentionally excluded from `get<T>()`/generic conversion support, so `get<std::any>()` and
containers like `std::map<std::string, std::any>` fail to compile by design -- there is no way to know, from a
containers like `std::map<std::string, std::any>` fail to compile by design — there is no way to know, from a
`json` value alone, which concrete type to store inside the `std::any`. To work with heterogeneous JSON values,
dispatch on the value's type manually and construct the `std::any` (or extract from it) yourself:
@@ -470,7 +497,7 @@ what the library does instead.
## Why does `std::wstring` convert or dump incorrectly?
The library assumes UTF-8 encoding internally, so `std::wstring` is not supported out of the box -- see the FAQ
The library assumes UTF-8 encoding internally, so `std::wstring` is not supported out of the box — see the FAQ
entry on [wide string handling](../home/faq.md#wide-string-handling) for why, and for a UTF-8 conversion recipe.
## Can I write my own serializer? (Advanced use)
@@ -536,3 +563,12 @@ struct bad_serializer
- [Specializing enum conversion](enum_conversion.md) - map enums to JSON strings instead of integers
- [Supported macros](macros.md) - reference for `NLOHMANN_DEFINE_TYPE_*` and related macros
- [`adl_serializer`](../api/adl_serializer/index.md) - the default `JSONSerializer` used in the conversion dispatch
- [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](../api/macros/nlohmann_define_type_intrusive.md) - generate the conversion functions inside the type
- [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](../api/macros/nlohmann_define_type_non_intrusive.md) - generate the conversion functions outside the type
- [`NLOHMANN_DEFINE_TYPE_WITH_NAMES`](../api/macros/nlohmann_define_type_with_names.md) - use JSON keys that differ from the member names
- [`NLOHMANN_DEFINE_DERIVED_TYPE`](../api/macros/nlohmann_define_derived_type.md) - generate the conversion functions for derived classes
- [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md) - map enum values to JSON values
- [`get`](../api/basic_json/get.md) - convert a JSON value to a C++ type
- [`get_to`](../api/basic_json/get_to.md) - convert a JSON value into an existing variable
- [`json_serializer`](../api/basic_json/json_serializer.md) - the `JSONSerializer` template parameter
+5 -1
View File
@@ -167,4 +167,8 @@ result in undefined behavior. Since version 3.12.0, this library checks for `nul
## See also
- [JSON_ASSERT](../api/macros/json_assert.md) - control behavior of runtime assertions
- [`JSON_ASSERT`](../api/macros/json_assert.md) - control behavior of runtime assertions
- [Supported Macros](macros.md) - all supported macros
- [Working Without Exceptions](no_exceptions.md) - using the library without exceptions
- [Exceptions](../home/exceptions.md) - all exceptions and their error codes
- [Macros](../api/macros/index.md) - reference of all macros
@@ -338,3 +338,12 @@ The library maps BJData types to JSON value types as follows:
```json
--8<-- "examples/from_bjdata.output"
```
## See also
- [Binary formats](index.md) - overview and comparison of the supported binary formats
- [Binary values](../binary_values.md) - how binary values and subtypes map to BJData
- [`to_bjdata`](../../api/basic_json/to_bjdata.md) - serialize a JSON value to BJData
- [`from_bjdata`](../../api/basic_json/from_bjdata.md) - parse a JSON value from BJData
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
@@ -201,3 +201,12 @@ of the message are reported as [parse_error.110](../../home/exceptions.md#jsonex
```json
--8<-- "examples/from_bon8.output"
```
## See also
- [Binary formats](index.md) - overview and comparison of the supported binary formats
- [Binary values](../binary_values.md) - how binary values and subtypes map to BON8
- [`to_bon8`](../../api/basic_json/to_bon8.md) - serialize a JSON value to BON8
- [`from_bon8`](../../api/basic_json/from_bon8.md) - parse a JSON value from BON8
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
@@ -204,3 +204,12 @@ The library maps BSON record types to JSON value types as follows:
```json
--8<-- "examples/from_bson.output"
```
## See also
- [Binary formats](index.md) - overview and comparison of the supported binary formats
- [Binary values](../binary_values.md) - how binary values and subtypes map to BSON
- [`to_bson`](../../api/basic_json/to_bson.md) - serialize a JSON value to BSON
- [`from_bson`](../../api/basic_json/from_bson.md) - parse a JSON value from BSON
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
@@ -287,3 +287,13 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
```json
--8<-- "examples/from_cbor.output"
```
## See also
- [Binary formats](index.md) - overview and comparison of the supported binary formats
- [Binary values](../binary_values.md) - how binary values and subtypes map to CBOR
- [`to_cbor`](../../api/basic_json/to_cbor.md) - serialize a JSON value to CBOR
- [`from_cbor`](../../api/basic_json/from_cbor.md) - parse a JSON value from CBOR
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
- [`cbor_tag_handler_t`](../../api/basic_json/cbor_tag_handler_t.md) - how CBOR tags are handled when parsing
@@ -53,11 +53,32 @@ See [binary values](../binary_values.md) for more information.
| UBJSON (size) | 58.6 % | 92.3 % | 86.8 % | 97.4 % |
| UBJSON (size+type) | 55.9 % | 92.3 % | 85.0 % | 95.0 % |
Sizes compared to minified JSON value; the smallest size per file is printed in bold.
<p class="nj-table-caption" markdown>Size of each test file in each format, in percent of the minified JSON value; the smallest size per file is printed in bold. The (size) and (size+type) rows are written with the `use_size` and `use_type` options of [`to_bjdata`](../../api/basic_json/to_bjdata.md) and [`to_ubjson`](../../api/basic_json/to_ubjson.md).</p>
<div class="nj-charts-grid-wrap" markdown>
<div class="nj-fig-wrap" markdown>
--8<-- "images/binary_sizes_canada.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/binary_sizes_twitter.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/binary_sizes_citm_catalog.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/binary_sizes_jeopardy.svg"
</div>
</div>
<p class="nj-charts-caption" markdown>The table above as charts: size per base format relative to minified JSON (vertical line at
100 %); the smallest format per file is drawn in a stronger colour than the others. The size and size+type variants of BJData and UBJSON are
omitted here; see the table.</p>
## See also
- [`cbor_tag_handler_t`](../../api/basic_json/cbor_tag_handler_t.md) - how CBOR tags are handled when parsing
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [Binary values](../binary_values.md) - binary values and subtypes
- [**to_cbor**](../../api/basic_json/to_cbor.md) / [**from_cbor**](../../api/basic_json/from_cbor.md), [**to_msgpack**](../../api/basic_json/to_msgpack.md) / [**from_msgpack**](../../api/basic_json/from_msgpack.md), [**to_bson**](../../api/basic_json/to_bson.md) / [**from_bson**](../../api/basic_json/from_bson.md), [**to_ubjson**](../../api/basic_json/to_ubjson.md) / [**from_ubjson**](../../api/basic_json/from_ubjson.md), [**to_bjdata**](../../api/basic_json/to_bjdata.md) / [**from_bjdata**](../../api/basic_json/from_bjdata.md), [**to_bon8**](../../api/basic_json/to_bon8.md) / [**from_bon8**](../../api/basic_json/from_bon8.md) - the conversion functions
- [Parsing untrusted input](../parsing/untrusted_input.md)
- [`to_cbor`](../../api/basic_json/to_cbor.md) - / [`from_cbor`](../../api/basic_json/from_cbor.md), [`to_msgpack`](../../api/basic_json/to_msgpack.md) / [`from_msgpack`](../../api/basic_json/from_msgpack.md), [`to_bson`](../../api/basic_json/to_bson.md) / [`from_bson`](../../api/basic_json/from_bson.md), [`to_ubjson`](../../api/basic_json/to_ubjson.md) / [`from_ubjson`](../../api/basic_json/from_ubjson.md), [`to_bjdata`](../../api/basic_json/to_bjdata.md) / [`from_bjdata`](../../api/basic_json/from_bjdata.md), [`to_bon8`](../../api/basic_json/to_bon8.md) / [`from_bon8`](../../api/basic_json/from_bon8.md) - the conversion functions
- [Parsing untrusted input](../parsing/untrusted_input.md) - harden an application that parses untrusted binary data
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
@@ -242,3 +242,12 @@ written as the reserved negative ext types (255 as -1), so `json::binary({1,2,3,
```json
--8<-- "examples/from_msgpack.output"
```
## See also
- [Binary formats](index.md) - overview and comparison of the supported binary formats
- [Binary values](../binary_values.md) - how binary values and subtypes map to MessagePack
- [`to_msgpack`](../../api/basic_json/to_msgpack.md) - serialize a JSON value to MessagePack
- [`from_msgpack`](../../api/basic_json/from_msgpack.md) - parse a JSON value from MessagePack
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
@@ -221,3 +221,12 @@ The library maps UBJSON types to JSON value types as follows:
```json
--8<-- "examples/from_ubjson.output"
```
## See also
- [Binary formats](index.md) - overview and comparison of the supported binary formats
- [Binary values](../binary_values.md) - how binary values and subtypes map to UBJSON
- [`to_ubjson`](../../api/basic_json/to_ubjson.md) - serialize a JSON value to UBJSON
- [`from_ubjson`](../../api/basic_json/from_ubjson.md) - parse a JSON value from UBJSON
- [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) - reject ill-formed UTF-8 in strings
- [SAX interface](../parsing/sax_interface.md) - decode binary formats without building a value
@@ -25,6 +25,8 @@ class vector ["std::vector<uint8_t>"]
vector <|-- binary_t
```
<p class="nj-caption" markdown>Class diagram of `json::binary_t`: it derives from `std::vector<uint8_t>` and adds functions to set, clear, query, and test an optional subtype.</p>
By default, binary values are stored as `std::vector<std::uint8_t>`. This type can be changed by providing a template
parameter to the `basic_json` type. To store binary subtypes, the storage type is extended and exposed as
[`json::binary_t`](../api/basic_json/binary_t.md):
@@ -430,3 +432,13 @@ as an array of uint8 values. The library implements this translation.
"binary": [202, 254, 186, 190]
}
```
## See also
- [Binary formats](binary_formats/index.md) - the formats that can store binary values
- [`binary_t`](../api/basic_json/binary_t.md) - type for binary values
- [`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) - container for binary values with subtype
- [`binary`](../api/basic_json/binary.md) - create a binary value
- [`get_binary`](../api/basic_json/get_binary.md) - access the stored binary value
- [`is_binary`](../api/basic_json/is_binary.md) - check whether a value is binary
- [`cbor_tag_handler_t`](../api/basic_json/cbor_tag_handler_t.md) - store or ignore CBOR tags when parsing
+8 -1
View File
@@ -11,7 +11,7 @@ This library does not support comments *by default*. It does so for three reason
3. It is dangerous for interoperability if some libraries add comment support while others do not. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this.
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. Combined with [`ignore_trailing_commas`](trailing_commas.md) (also a `parse` parameter), this covers what is commonly referred to as **JSONC** (JSON with Comments, as used e.g. by Visual Studio Code's `.jsonc` files) -- comments and trailing commas, nothing more. This is a different, smaller extension than [JSON5](https://json5.org), which additionally allows unquoted keys, single-quoted strings, and other syntax changes that this library does not support.
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. Combined with [`ignore_trailing_commas`](trailing_commas.md) (also a `parse` parameter), this covers what is commonly referred to as **JSONC** (JSON with Comments, as used e.g. by Visual Studio Code's `.jsonc` files) — comments and trailing commas, nothing more. This is a different, smaller extension than [JSON5](https://json5.org), which additionally allows unquoted keys, single-quoted strings, and other syntax changes that this library does not support.
For more information, see [JSON With Commas and Comments (JWCC)](https://nigeltao.github.io/blog/2021/json-with-commas-comments.html).
@@ -38,3 +38,10 @@ For more information, see [JSON With Commas and Comments (JWCC)](https://nigelta
```
--8<-- "examples/comments.output"
```
## See also
- [`parse`](../api/basic_json/parse.md) - deserialize from a compatible input
- [Trailing Commas](trailing_commas.md) - accept trailing commas in arrays and objects
- [Parsing](parsing/index.md) - overview of the parsing functions
- [Parsing Untrusted Input](parsing/untrusted_input.md) - limits and checks for input you do not control
+10 -5
View File
@@ -177,7 +177,7 @@ which forces the explicit `get` form and can catch unintended conversions at com
`std::optional<T>` from delegating to `get<T>()`, to avoid a constructor ambiguity with
`std::optional<T>`'s own converting constructor from `basic_json`. As a result,
`static_cast<std::optional<T>>(json_value)` goes through `std::optional<T>`'s own converting
constructor rather than through `get<std::optional<T>>()`, which can behave differently -- for example,
constructor rather than through `get<std::optional<T>>()`, which can behave differently — for example,
with a custom `adl_serializer<std::optional<T>>` specialization. Prefer `get<std::optional<T>>()`/`get_to()`
over `static_cast` for optional types.
@@ -193,7 +193,7 @@ which forces the explicit `get` form and can catch unintended conversions at com
- The JSON array has **too many** elements: the surplus is **silently discarded**, and no exception is
thrown.
- The JSON array has **too few** elements: `at` throws
[`out_of_range.401`](../home/exceptions.md#jsonexceptionout_of_range401) for the first missing index --
[`out_of_range.401`](../home/exceptions.md#jsonexceptionout_of_range401) for the first missing index —
an out-of-range error, not a [`type_error`](../home/exceptions.md#type-errors), even though the cause
is a shape mismatch.
@@ -211,7 +211,7 @@ which forces the explicit `get` form and can catch unintended conversions at com
## Omitting a field when serializing `std::optional`
By default, `to_json` for `std::optional<T>` writes either the value or `#!json null` -- there is no built-in way
By default, `to_json` for `std::optional<T>` writes either the value or `#!json null` — there is no built-in way
to make a field disappear from the serialized object entirely when the `std::optional` is `std::nullopt`. Because
a specialization of `adl_serializer<std::optional<T>>` only controls how the *value* is converted (it cannot
prevent the containing object's `to_json` from inserting the key in the first place), omission has to be
@@ -243,7 +243,7 @@ json j = numbers; // [1,2,3]
!!! info "Constructing from a C++20 range view"
A `json` array can also be constructed directly from a C++20 range view (`std::ranges::view`), such as the result
of `std::views::filter` or `std::views::transform` -- no intermediate container is needed:
of `std::views::filter` or `std::views::transform` — no intermediate container is needed:
```cpp
std::vector<int> nums{1, 2, 37, 42, 21};
@@ -266,7 +266,12 @@ The conversions above are built in for standard types. To make the same syntax w
- [`get`](../api/basic_json/get.md) - get a copy converted to a given type
- [`get_to`](../api/basic_json/get_to.md) - convert into an existing variable
- [`get_ref`](../api/basic_json/get_ref.md) / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
- [`get_ref`](../api/basic_json/get_ref.md) - / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
- [Zero-copy JSON views](json_view.md) - get values out of a document or a view
- [`adl_serializer`](../api/adl_serializer/index.md) - the default serializer behind the conversions
- [`type`](../api/basic_json/type.md) - query the type of a value
- [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) - toggle conversion to tuples of references
- [`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md) - toggle conversion from ranges
+6 -1
View File
@@ -118,8 +118,13 @@ string `#!json "42"`.
## See also
- [`basic_json` constructors](../api/basic_json/basic_json.md) - all ways to construct a value
- [`array`](../api/basic_json/array.md) / [`object`](../api/basic_json/object.md) - force array or object type
- [`array`](../api/basic_json/array.md) - / [`object`](../api/basic_json/object.md) - force array or object type
- [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal
- [Converting values](conversions.md) - which C++ types can be used
- [Parsing](parsing/index.md) - create a value from JSON text
- [Zero-copy JSON views](json_view.md) - create a document and a view from JSON text instead of a value
- [`basic_json`](../api/basic_json/index.md) - the class template and its member functions
- [Types](types/index.md) - the JSON value types
- [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) - change how a single braced value is interpreted
- [`operator=`](../api/basic_json/operator=.md) - assign a new value
@@ -89,3 +89,13 @@ non-existing, an exception is thrown.
| access to valid array index | reference to existing value is returned | const reference to existing value is returned |
| access to non-existing object key | `basic_json::out_of_range` exception is thrown | `basic_json::out_of_range` exception is thrown |
| access to invalid array index | `basic_json::out_of_range` exception is thrown | `basic_json::out_of_range` exception is thrown |
<p class="nj-table-caption" markdown>Result of [`at`](../../api/basic_json/at.md) in each scenario, for a non-const and a const value.</p>
## See also
- [Element Access](index.md) - overview of all ways to access elements
- [Unchecked access: operator[]](unchecked_access.md) - access without range checking
- [Access with default value: value](default_value.md) - fall back to a default instead of throwing
- [`at`](../../api/basic_json/at.md) - checked access by reference
- [JSON Pointer](../json_pointer.md) - access nested values with `at` and a pointer
@@ -127,8 +127,10 @@ containing `.` need a different separator.
## See also
- [`value`](../../api/basic_json/value.md) for access with default value
- documentation on [checked access](checked_access.md)
- documentation on [JSON Pointer](../json_pointer.md)
- [`contains`](../../api/basic_json/contains.md) to check whether a key or JSON Pointer exists
- [`json_pointer::operator/=`](../../api/json_pointer/operator_slasheq.md) to build a pointer token by token
- [`value`](../../api/basic_json/value.md) - for access with default value
- [Checked access: at](checked_access.md) - access with bounds checking
- [JSON Pointer](../json_pointer.md) - addressing values by path
- [`contains`](../../api/basic_json/contains.md) - to check whether a key or JSON Pointer exists
- [`json_pointer::operator/=`](../../api/json_pointer/operator_slasheq.md) - to build a pointer token by token
- [Unchecked access: operator[]](unchecked_access.md) - access without range checking
- [Element Access](index.md) - overview of all ways to access elements
@@ -12,16 +12,23 @@ Testing whether a key or index exists before accessing it is also possible, with
[`contains`](../../api/basic_json/contains.md) or [`find`](../../api/basic_json/find.md) (which returns an iterator to
the value, or `end()` if it is not found).
How do I get a value out? Start at the top and follow the first question that applies:
```mermaid
flowchart TD
A["accessing a value"] --> B{"must it exist?"}
B -->|"yes, missing is an error"| C["at() -- throws"]
B -->|"yes, but checking is my job"| D["operator[] -- unchecked"]
B -->|"no, a fallback is fine"| E["value() -- default value"]
A --> F{"just testing first?"}
F -->|"yes"| G["contains() / find()"]
A["getting a value out"] --> P{{"nested path such as /server/port?"}}
P -->|"yes"| JP["JSON Pointer: at(ptr), value(ptr, default), contains(ptr)"]
P -->|"no, one key or index"| T{{"only testing whether it exists?"}}
T -->|"yes"| CF["contains() / find()"]
T -->|"no"| M{{"may it be missing?"}}
M -->|"yes, a fallback is fine"| V["value(key, default)"]
M -->|"yes, and missing is an error"| AT["at(): throws out_of_range"]
M -->|"no, it exists and its type is known"| OP["operator[] and get#lt;T#gt;()"]
OP -.- W["non-const operator[] inserts null for a missing key"]
```
<p class="nj-caption" markdown>Decision tree for reading a value: the labels on the arrows are the answers to the questions, and the boxes name the functions to use. The dashed line marks a side effect of the non-const `operator[]`.</p>
## Does this key exist?
Use [`contains`](../../api/basic_json/contains.md) to test for a key; it does not modify the value and does not throw:
@@ -63,9 +70,16 @@ if (auto it = j.find("name"); it != j.end())
## See also
- [**operator[]**](../../api/basic_json/operator%5B%5D.md) - unchecked access
- [**at**](../../api/basic_json/at.md) - checked access
- [**value**](../../api/basic_json/value.md) - access with default value
- [**contains**](../../api/basic_json/contains.md) and [**find**](../../api/basic_json/find.md) - test for existence
- [**front**](../../api/basic_json/front.md) and [**back**](../../api/basic_json/back.md) - first and last element
- [**items**](../../api/basic_json/items.md) - iterate over keys and values
- [`operator[]`](../../api/basic_json/operator%5B%5D.md) - unchecked access
- [`at`](../../api/basic_json/at.md) - checked access
- [`value`](../../api/basic_json/value.md) - access with default value
- [`contains`](../../api/basic_json/contains.md) - and [`find`](../../api/basic_json/find.md) - test for existence
- [`front`](../../api/basic_json/front.md) - and [`back`](../../api/basic_json/back.md) - first and last element
- [`items`](../../api/basic_json/items.md) - iterate over keys and values
- [Checked access: at](checked_access.md) - access with range checking
- [Unchecked access: operator[]](unchecked_access.md) - access without range checking
- [Access with default value: value](default_value.md) - fall back to a default instead of throwing
- [Iterators](../iterators.md) - visit all elements of arrays and objects
- [JSON Pointer](../json_pointer.md) - address nested values with a single string
- [Modifying values](../modifying_values.md) - add, merge and remove elements
- [`basic_json`](../../api/basic_json/index.md) - all member functions
@@ -125,3 +125,13 @@ for (int i = 0; i < 1000; ++i) {
| access to valid array index | reference to existing value is returned | const reference to existing value is returned |
| access to non-existing object key | reference to newly inserted `#!json null` value is returned | **undefined behavior**; [runtime assertion](../assertions.md) in debug mode |
| access to invalid array index | reference to newly inserted `#!json null` value is returned; any index between previous maximal index and passed index are filled with `#!json null` | **undefined behavior**; [runtime assertion](../assertions.md) in debug mode |
<p class="nj-table-caption" markdown>Result of [`operator[]`](../../api/basic_json/operator%5B%5D.md) in each scenario, for a non-const and a const value.</p>
## See also
- [Element Access](index.md) - overview of all ways to access elements
- [Checked access: at](checked_access.md) - access with range checking
- [Access with default value: value](default_value.md) - fall back to a default instead of throwing
- [`operator[]`](../../api/basic_json/operator%5B%5D.md) - unchecked access by reference
- [Runtime assertions](../assertions.md) - assertions that guard `operator[]` misuse
@@ -108,3 +108,12 @@ Other Important points:
```json
--8<-- "examples/nlohmann_json_serialize_enum_strict_err.output"
```
## See also
- [Arbitrary Type Conversions](arbitrary_types.md) - convert your own types
- [Converting values](conversions.md) - the general overview of `get`/`get_to`
- [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md) - define the enum-to-JSON mapping
- [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../api/macros/nlohmann_json_serialize_enum_strict.md) - throw on unmapped values
- [`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md) - disable the default enum conversion
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) - serialize maps with enum keys as objects
+16 -2
View File
@@ -10,7 +10,8 @@ C++ types, and finally serialize it again.
understand the `#!cpp {}` vs. `#!cpp []` ambiguity.
- [Parsing](parsing/index.md) — read a JSON value from a string, file, or stream, including
[JSON Lines](parsing/json_lines.md), [callbacks](parsing/parser_callbacks.md), the
[SAX interface](parsing/sax_interface.md), [error handling](parsing/parse_exceptions.md), and
[SAX interface](parsing/sax_interface.md), [error handling](parsing/parse_exceptions.md),
[large and partial input](parsing/large_and_partial_input.md), and
[parsing untrusted input](parsing/untrusted_input.md).
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
strings and numbers stay in the input and are only decoded when needed.
@@ -33,6 +34,8 @@ C++ types, and finally serialize it again.
- [Converting values](conversions.md) — get values out with [`get`](../api/basic_json/get.md)/[`get_to`](../api/basic_json/get_to.md),
and understand implicit conversions.
- [Arbitrary types conversions](arbitrary_types.md) — teach the library about your own structs and classes.
- [Recipes for common types](type_recipes.md) — convert smart pointers, `std::variant`, `std::chrono` types, and class
hierarchies, which the library deliberately leaves to you.
- [Specializing enum conversion](enum_conversion.md) — map enums to strings instead of integers.
## Serializing values
@@ -52,6 +55,7 @@ C++ types, and finally serialize it again.
template parameters has to provide.
- [Object order](object_order.md) — keep insertion order with [`ordered_json`](../api/ordered_json.md).
- [Performance](performance.md) — practical advice on parsing, memory use, serialization, and compile times.
- [Working without exceptions](no_exceptions.md) — what to use when exceptions are unwanted or unavailable.
- [Runtime assertions](assertions.md), [supported macros](macros.md), the [`nlohmann` namespace](namespace.md), and
[C++ modules](modules.md) — build-time and runtime configuration.
@@ -65,5 +69,15 @@ C++ types, and finally serialize it again.
- [API documentation](../api/basic_json/index.md) - reference of all classes and functions
- [FAQ](../home/faq.md) - answers to common questions
- [Exceptions](../home/exceptions.md) - all exceptions and their error codes
- [Design goals](../home/design_goals.md)
- [Design goals](../home/design_goals.md) - the goals behind the library
- [Integration](../integration/index.md) - how to add the library to a project
- [Macros](../api/macros/index.md) - reference of all macros
- [`json_pointer`](../api/json_pointer/index.md) - the JSON Pointer class
- [`adl_serializer`](../api/adl_serializer/index.md) - customization point for type conversions
- [`json_sax`](../api/json_sax/index.md) - interface of the SAX parser events
- [`json_document`](../api/json_document.md) - an immutable document with a flat index
- [`json_view`](../api/json_view.md) - a non-owning view of a value in a document
- [`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) - binary values with a subtype
- [`ordered_json`](../api/ordered_json.md) - a JSON type preserving insertion order
- [Supported Macros](macros.md) - all supported macros
- [`nlohmann` Namespace](namespace.md) - the library namespace
+20 -2
View File
@@ -9,7 +9,11 @@ As for other containers, [`begin()`](../api/basic_json/begin.md) returns an iter
placeholder and cannot be dereferenced. In case of null values, empty arrays, or empty objects, `begin()` will return
`end()`.
![Illustration from cppreference.com](../images/range-begin-end.svg)
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
<p class="nj-caption" markdown>The range of `begin()` and `end()`: `begin()` refers to the first element, `end()` to the position after the last element, which must not be dereferenced.</p>
### Iteration order for objects
@@ -112,7 +116,11 @@ for (auto& [key, val] : j_object.items())
[`rbegin()`](../api/basic_json/rbegin.md) and [`rend()`](../api/basic_json/rend.md) return iterators in the reverse
sequence.
![Illustration from cppreference.com](../images/range-rbegin-rend.svg)
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
<p class="nj-caption" markdown>The range of `rbegin()` and `rend()` over the same elements: `rbegin()` refers to the last element and `rend()` to the position before the first one. Each reverse iterator stores an iterator to the element after the one it refers to.</p>
??? example "Example: reverse iteration with `rbegin()` and `rend()`"
@@ -159,3 +167,13 @@ Note that "value" means a JSON value in this setting, not values stored in the u
| Operations | invalidated iterators |
|------------|-----------------------|
| `clear` | all |
## See also
- [`begin`](../api/basic_json/begin.md) - / [`end`](../api/basic_json/end.md) - iterators to the first and one past the last element
- [`rbegin`](../api/basic_json/rbegin.md) - / [`rend`](../api/basic_json/rend.md) - reverse iterators
- [`cbegin`](../api/basic_json/cbegin.md) - / [`cend`](../api/basic_json/cend.md) - const iterators
- [`items`](../api/basic_json/items.md) - iterate over keys and values
- [Object Order](object_order.md) - iteration order of object keys
- [Element Access](element_access/index.md) - access single elements
- [Modifying values](modifying_values.md) - add and remove elements (including iterator invalidation rules)
+23
View File
@@ -7,6 +7,24 @@ of operations to apply to a JSON document. Operations address locations in the d
[JSON Pointer](json_pointer.md) paths. With the [`patch`](../api/basic_json/patch.md) function, a JSON Patch is applied
to the current JSON value by executing all operations from the patch, yielding the patched document as a new value.
The following steps through one operation of each kind. The first three operations are those of the example below; the
other three continue on its result.
<div class="nj-patch" data-nj-patch markdown>
| Operation | Before | After |
|-------------------------------------------------------------|------------------------------------------------|-------------------------------------------------|
| `{"op": "replace", "path": "/baz", "value": "boo"}` | `{"baz": "qux", "foo": "bar"}` | `{"baz": "boo", "foo": "bar"}` |
| `{"op": "add", "path": "/hello", "value": ["world"]}` | `{"baz": "boo", "foo": "bar"}` | `{"baz": "boo", "foo": "bar", "hello": ["world"]}` |
| `{"op": "remove", "path": "/foo"}` | `{"baz": "boo", "foo": "bar", "hello": ["world"]}` | `{"baz": "boo", "hello": ["world"]}` |
| `{"op": "move", "from": "/baz", "path": "/hello/1"}` | `{"baz": "boo", "hello": ["world"]}` | `{"hello": ["world", "boo"]}` |
| `{"op": "copy", "from": "/hello/0", "path": "/greeting"}` | `{"hello": ["world", "boo"]}` | `{"greeting": "world", "hello": ["world", "boo"]}` |
| `{"op": "test", "path": "/greeting", "value": "world"}` | `{"greeting": "world", "hello": ["world", "boo"]}` | unchanged (a failed test throws) |
</div>
<p class="nj-caption" markdown>Stepper through the six operations: *Next* applies the next operation, *Back* undoes it, and *Reset* returns to the original document. The current operation is highlighted in the patch list, and the lines it changes are marked in the document.</p>
!!! tip "Applying a patch without copying"
[`patch`](../api/basic_json/patch.md) leaves the original value unchanged and returns the patched result as a copy.
@@ -45,6 +63,8 @@ flowchart LR
P -.->|"applied to source, yields"| T
```
<p class="nj-caption" markdown>How `diff` and `patch` relate: `diff(source, target)` yields a patch, and applying that patch to the source yields the target.</p>
!!! success "Invariant"
For two JSON values *source* and *target*, the following code yields true:
@@ -85,3 +105,6 @@ flowchart LR
- [`patch`](../api/basic_json/patch.md) - apply a JSON Patch, returning the result as a copy
- [`patch_inplace`](../api/basic_json/patch_inplace.md) - apply a JSON Patch without copying
- [`diff`](../api/basic_json/diff.md) - compute a JSON Patch from two values
- [`json_pointer`](../api/json_pointer/index.md) - type to represent JSON Pointers
- [`merge_diff`](../api/basic_json/merge_diff.md) - compute a JSON Merge Patch from two values
- [Modifying values](modifying_values.md) - simple in-place modifications
+49 -5
View File
@@ -18,6 +18,40 @@ Consider the following JSON document
}
```
Select a pointer (or a node of the tree) to walk it through this document; each segment of the pointer goes one level
deeper. The table below lists the same pointers.
<div class="nj-pointer-walk" data-nj-pointer-walk data-doc="{&quot;array&quot;:[&quot;A&quot;,&quot;B&quot;,&quot;C&quot;],&quot;nested&quot;:{&quot;one&quot;:1,&quot;two&quot;:2,&quot;three&quot;:[true,false]}}" role="group" aria-label="Pointer walker for the example document">
<p class="nj-pointer-static">Highlighted: the path of the pointer <code>/nested/three/1</code>, which identifies <code>false</code>.</p>
<ul class="nj-pointer-tree">
<li data-p="" class="is-on"><span class="nj-pointer-n"><span class="nj-pointer-k">root</span> <span class="nj-pointer-t">object</span></span>
<ul>
<li data-p="/array" class="is-pass"><span class="nj-pointer-n"><span class="nj-pointer-k">&quot;array&quot;</span> <span class="nj-pointer-t">array</span></span>
<ul>
<li data-p="/array/0"><span class="nj-pointer-n"><span class="nj-pointer-k">0</span>: <span class="nj-pointer-v nj-pointer-v--s">&quot;A&quot;</span></span></li>
<li data-p="/array/1"><span class="nj-pointer-n"><span class="nj-pointer-k">1</span>: <span class="nj-pointer-v nj-pointer-v--s">&quot;B&quot;</span></span></li>
<li data-p="/array/2"><span class="nj-pointer-n"><span class="nj-pointer-k">2</span>: <span class="nj-pointer-v nj-pointer-v--s">&quot;C&quot;</span></span></li>
</ul>
</li>
<li data-p="/nested" class="is-on"><span class="nj-pointer-n"><span class="nj-pointer-k">&quot;nested&quot;</span> <span class="nj-pointer-t">object</span></span>
<ul>
<li data-p="/nested/one" class="is-pass"><span class="nj-pointer-n"><span class="nj-pointer-k">&quot;one&quot;</span>: <span class="nj-pointer-v nj-pointer-v--n">1</span></span></li>
<li data-p="/nested/two" class="is-pass"><span class="nj-pointer-n"><span class="nj-pointer-k">&quot;two&quot;</span>: <span class="nj-pointer-v nj-pointer-v--n">2</span></span></li>
<li data-p="/nested/three" class="is-on"><span class="nj-pointer-n"><span class="nj-pointer-k">&quot;three&quot;</span> <span class="nj-pointer-t">array</span></span>
<ul>
<li data-p="/nested/three/0" class="is-pass"><span class="nj-pointer-n"><span class="nj-pointer-k">0</span>: <span class="nj-pointer-v nj-pointer-v--b">true</span></span></li>
<li data-p="/nested/three/1" class="is-on is-target"><span class="nj-pointer-n"><span class="nj-pointer-k">1</span>: <span class="nj-pointer-v nj-pointer-v--b">false</span></span></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<p class="nj-caption" markdown>The highlighted nodes are the path from the root to the selected level, and the line below the tree shows the value the pointer resolves to. Select a segment in the pointer bar to stop at a shallower level, or a node of the tree to select its pointer.</p>
Then every value inside the JSON document can be identified as follows:
| JSON Pointer | JSON value |
@@ -138,12 +172,22 @@ The reverse function, [`unflatten`](../api/basic_json/unflatten.md) recreates th
auto j_original = j_flat.unflatten();
```
Each leaf of the document becomes one key of the flat object, and `unflatten()` reverses the mapping:
<div class="nj-fig-wrap" markdown>
--8<-- "images/json_pointer_flatten.svg"
</div>
<p class="nj-caption" markdown>Left: the example document as a tree; right: the flat object it becomes. `flatten()` converts from left to right, `unflatten()` from right to left.</p>
## See also
- Class [`json_pointer`](../api/json_pointer/index.md)
- Functions [`value`](../api/basic_json/value.md), [`contains`](../api/basic_json/contains.md), and
[`at`](../api/basic_json/at.md) accept JSON Pointers; see [Nested values](element_access/default_value.md#nested-values)
- Function [`flatten`](../api/basic_json/flatten.md)
- Function [`unflatten`](../api/basic_json/unflatten.md)
- [`json_pointer`](../api/json_pointer/index.md) - the JSON Pointer class
- [Nested values](element_access/default_value.md#nested-values) - [`value`](../api/basic_json/value.md),
[`contains`](../api/basic_json/contains.md), and [`at`](../api/basic_json/at.md) accept JSON Pointers
- [`flatten`](../api/basic_json/flatten.md) - convert a value to an object of JSON Pointers and primitive values
- [`unflatten`](../api/basic_json/unflatten.md) - restore a value from a flattened object
- [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers
- [JSON Merge Patch](merge_patch.md) - an alternative patch format that does not use JSON Pointer
- [`operator""_json_pointer`](../api/operator_literal_json_pointer.md) - user-defined literal to create a JSON pointer
- [Element Access](element_access/index.md) - other ways to access elements
+116 -32
View File
@@ -21,18 +21,26 @@ referenced in place in the source text: a float is converted when it is read, an
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 --
index. It gives you the read-only, type-inspection part of the `basic_json` interface —
[`type()`](../api/basic_json_view/type.md) and the `is_*()` predicates,
[`size()`](../api/basic_json_view/size.md)/[`empty()`](../api/basic_json_view/empty.md) -- as well as element access
[`size()`](../api/basic_json_view/size.md)/[`empty()`](../api/basic_json_view/empty.md) — as well as element access
([`operator[]`](../api/basic_json_view/operator%5B%5D.md), [`at`](../api/basic_json_view/at.md),
[`front`](../api/basic_json_view/front.md)/[`back`](../api/basic_json_view/back.md)), lookup
([`find`](../api/basic_json_view/find.md), [`contains`](../api/basic_json_view/contains.md),
[`count`](../api/basic_json_view/count.md)), and iteration
([`begin`](../api/basic_json_view/begin.md)/[`end`](../api/basic_json_view/end.md),
[`items`](../api/basic_json_view/items.md)) -- without ever allocating a `basic_json` value. When you do need an
[`items`](../api/basic_json_view/items.md)) — without ever allocating a `basic_json` value. When you do need an
actual `basic_json` value for a subtree, [`materialize()`](../api/basic_json_view/materialize.md) builds exactly the
one [`parse()`](../api/basic_json/parse.md) would have produced for it.
The same small text, parsed both ways:
<div class="nj-fig-wrap" markdown>
--8<-- "images/json_view_layout.svg"
</div>
<p class="nj-caption" markdown>Top: the tree of separate heap allocations that `basic_json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.</p>
## How to use it
Include `<nlohmann/json_view.hpp>` in addition to (or instead of) `<nlohmann/json.hpp>`. Parse into a
@@ -62,7 +70,7 @@ A document either **borrows** the text it was parsed from, or **owns** its own c
[`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
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"
@@ -84,10 +92,16 @@ Moving the `basic_json_document<>` itself is fine and does **not** invalidate it
allocation that keeps its address across the move. Take a fresh view from [`root()`](../api/basic_json_document/root.md)
whenever any of the other conditions above was not met.
<div class="nj-fig-wrap" markdown>
--8<-- "images/json_view_lifetime.svg"
</div>
<p class="nj-caption" markdown>Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.</p>
A view can dangle, exactly like a `#!cpp std::string_view`: nothing stops you from using it after its document is
gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `#!cpp std::shared_ptr` in
the document and a `#!cpp std::weak_ptr` in each view) would make it larger, add reference counting to every copy,
and a `lock()` to every access -- exactly the costs the view exists to avoid. What is caught is the obvious case:
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.
@@ -109,7 +123,7 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
[`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
- **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
@@ -155,7 +169,7 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
element access and lookup functions never carry the JSON Pointer path `JSON_DIAGNOSTICS` would otherwise add: the
view has no `basic_json` value to point at, so the exception is created without one, regardless of how
`BasicJsonType` was built.
- **Ordering comparisons are not provided** by `basic_json_view` -- there is no `#!cpp operator<`.
- **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
@@ -227,15 +241,15 @@ and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!
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
string as a `string_view_t` pointing into the document's [`source()`](../api/basic_json_document/source.md) text —
or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of
allocating a new `#!cpp std::string`.
- [`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
[`basic_json::parse()`](../api/basic_json/parse.md)) would.
Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is.
Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.
## Writing generic code
@@ -328,14 +342,14 @@ Two things differ when a `from_json()` reads the view:
[`dump()`](../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
`basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a
repeated key is written, not only the last one -- the same two ways [iteration](#what-is-different) already differs
repeated key is written, not only the last one — the same two ways [iteration](#what-is-different) already differs
from a [`materialize()`](../api/basic_json_view/materialize.md)d value, see [Duplicate keys](#duplicate-keys).
`#!cpp materialize().dump()` gives a different result in both respects for a `json_view`.
By default, numbers are written the way [`basic_json::dump()`](../api/basic_json/dump.md) would.
[`number_format::source`](../api/basic_json_view/number_format.md) instead copies every number exactly as it was
written in the source text -- a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
type holds, or a high-precision coordinate -- something `basic_json` cannot do at all, since parsing already reduces
written in the source text — a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
type holds, or a high-precision coordinate — something `basic_json` cannot do at all, since parsing already reduces
a number to its parsed `#!cpp double`/`#!cpp int64_t` value.
[`operator<<`](../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
@@ -344,14 +358,14 @@ the stream's `width`/`fill` for indentation.
## Editing a document
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but
not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](../api/basic_json_document/index.md) -- more conveniently
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
[`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
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.
@@ -361,15 +375,35 @@ The `#!cpp 19.90` price from [above](#writing-a-view-back) is exactly the kind o
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
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
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
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.
@@ -388,14 +422,14 @@ new value.
### 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
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 one of its elements is set,
appended to, inserted into, or erased, 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 --
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
@@ -404,16 +438,53 @@ edits. See [`basic_json_document`'s Edits](../api/basic_json_document/index.md#e
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
the index is described in the [architecture overview](../home/architecture.md#node-index-of-json-views).
Step through the edits of a small document (the node fields follow the [architecture overview](../home/architecture.md#node-index-of-json-views)):
<div class="nj-jsonview-steps" data-nj-jsonview-steps>
<figure class="nj-jsonview-step" data-label="After parsing">
<div class="nj-jsonview-scroll">
--8<-- "images/json_view_edit_0.svg"
</div>
<figcaption>Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.</figcaption>
</figure>
<figure class="nj-jsonview-step" data-label="set an existing member">
<div class="nj-jsonview-scroll">
--8<-- "images/json_view_edit_1.svg"
</div>
<figcaption>Step 2 of 5. <code>doc.set(doc.root(), "port", 6380)</code> overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.</figcaption>
</figure>
<figure class="nj-jsonview-step" data-label="erase an array element">
<div class="nj-jsonview-scroll">
--8<-- "images/json_view_edit_2.svg"
</div>
<figcaption>Step 3 of 5. <code>doc.erase(doc.root()["tags"], 0)</code>: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of <code>"eu"</code> stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.</figcaption>
</figure>
<figure class="nj-jsonview-step" data-label="push_back onto the array">
<div class="nj-jsonview-scroll">
--8<-- "images/json_view_edit_3.svg"
</div>
<figcaption>Step 4 of 5. <code>doc.push_back(doc.root()["tags"], "new")</code>: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.</figcaption>
</figure>
<figure class="nj-jsonview-step" data-label="set a new member">
<div class="nj-jsonview-scroll">
--8<-- "images/json_view_edit_4.svg"
</div>
<figcaption>Step 5 of 5. <code>doc.set(doc.root(), "retries", 3)</code>: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.</figcaption>
</figure>
</div>
<p class="nj-caption" markdown>The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.</p>
## Images
[`save()`](../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
[`load()`](../api/basic_json_document/load.md) reads back into a document without parsing -- no lexing, no building
[`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
**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.
@@ -424,10 +495,10 @@ 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
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
`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.
@@ -447,7 +518,7 @@ validation entirely, for an image trusted as much as the process's own memory. S
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*
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.
@@ -463,8 +534,21 @@ either.
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../api/basic_json_document/set.md)/[`push_back`](../api/basic_json_document/push_back.md)/[`insert`](../api/basic_json_document/insert.md)/[`erase`](../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../api/basic_json_view/number_format.md), the spelling of every untouched number |
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back -- a configuration file, for instance -- where the rest of it should come back exactly as it was |
| **Caching/reload** | not applicable -- re-parse, or roll your own serialization | not applicable | [`save()`](../api/basic_json_document/save.md)/[`load()`](../api/basic_json_document/load.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current -- possibly edited -- state |
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was |
| **Caching/reload** | not applicable — re-parse, or roll your own serialization | not applicable | [`save()`](../api/basic_json_document/save.md)/[`load()`](../api/basic_json_document/load.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current — possibly edited — state |
<p class="nj-table-caption" markdown>Each column is one way to work with JSON in this library, and each row compares one property of them.</p>
## See also
- [`basic_json_document`](../api/basic_json_document/index.md) - the document that owns the parsed index (parse, access, images, edits)
- [`basic_json_view`](../api/basic_json_view/index.md) - the non-owning handle to a value in a document
- [`json_document`](../api/json_document.md) - and [`ordered_json_document`](../api/ordered_json_document.md) - the read-only documents
- [`json_editable_document`](../api/json_editable_document.md) - and [`ordered_json_editable_document`](../api/ordered_json_editable_document.md) - the documents that can be edited in place
- [Architecture](../home/architecture.md#node-index-of-json-views) - the layout of the node index
- [SAX interface](parsing/sax_interface.md) - event-based parsing without building a tree
- [Performance](performance.md) - benchmarks and tuning of parsing and serialization
- [Large, Streamed, and Partial Input](parsing/large_and_partial_input.md) - reading only the part of a text you need
## Version history
+14
View File
@@ -322,3 +322,17 @@ These macros are defined by the library and contain the version numbers accordin
[Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
See [full documentation of `NLOHMANN_JSON_VERSION_MAJOR`, `NLOHMANN_JSON_VERSION_MINOR`, and `NLOHMANN_JSON_VERSION_PATCH`](../api/macros/nlohmann_json_version_major.md).
## See also
- [Macros](../api/macros/index.md) - reference of all macros
- [Runtime Assertions](assertions.md) - runtime assertions in detail
- [`nlohmann` Namespace](namespace.md) - the library namespace and its macros
- [Working Without Exceptions](no_exceptions.md) - using the library without exceptions
- [Modules](modules.md) - macros and C++20 modules
- [Exceptions](../home/exceptions.md) - all exceptions and their error codes
- [CMake](../integration/cmake.md) - set macros via CMake options
- [Compile times](../integration/compile_times.md) - reduce compile times with macros
- [Architecture](../home/architecture.md) - how the library is put together
- [Specializing enum conversion](enum_conversion.md) - enum macros
- [Arbitrary Type Conversions](arbitrary_types.md) - type conversion macros
+75 -2
View File
@@ -12,6 +12,78 @@ easier to read and write for object-shaped documents.
The function [`merge_diff`](../api/basic_json/merge_diff.md) creates a merge patch that changes one JSON value into
another.
The example below applies one patch to one document. Here is the document before:
```json
{
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": ["example", "sample"],
"content": "This will be unchanged"
}
```
This is the patch:
```json
{
"title": "Hello!",
"phoneNumber": "+01-123-456-7890",
"author": {
"familyName": null
},
"tags": ["example"]
}
```
And this is the document after applying it:
```json
{
"title": "Hello!",
"author": {
"givenName": "John"
},
"tags": ["example"],
"content": "This will be unchanged",
"phoneNumber": "+01-123-456-7890"
}
```
The table goes through the members one by one. "Member" is the name of an object member in the patch (and in the
document); it is not a [JSON Pointer](json_pointer.md) and nothing of it is passed to the function: the whole patch is
passed to [`merge_patch`](../api/basic_json/merge_patch.md).
<div class="nj-pointer-mpt" markdown>
| Rule | Member | Before | Patch | After |
|------|--------|--------|-------|-------|
| <span class="nj-pointer-mp nj-pointer-mp--set">change value</span> | `title` | `#!json "Goodbye!"` | `#!json "Hello!"` | `#!json "Hello!"` |
| <span class="nj-pointer-mp nj-pointer-mp--add">add key</span> | `phoneNumber` | <span class="nj-pointer-mp-none">(no member)</span> | `#!json "+01-123-456-7890"` | `#!json "+01-123-456-7890"` |
| <span class="nj-pointer-mp nj-pointer-mp--rec">object merges recursively</span> | `author` | `#!json {"givenName": "John", "familyName": "Doe"}` | `#!json {"familyName": null}` | `#!json {"givenName": "John"}` |
| <span class="nj-pointer-mp nj-pointer-mp--del">null removes key</span> | `familyName` inside `author` | `#!json "Doe"` | `#!json null` | <span class="nj-pointer-mp-none">(removed)</span> |
| <span class="nj-pointer-mp nj-pointer-mp--arr">array replaced whole</span> | `tags` | `#!json ["example", "sample"]` | `#!json ["example"]` | `#!json ["example"]` |
| <span class="nj-pointer-mp nj-pointer-mp--keep">unchanged key</span> | `content` | `#!json "This will be unchanged"` | <span class="nj-pointer-mp-none">(no member)</span> | `#!json "This will be unchanged"` |
</div>
An array in the patch replaces the array in the target, so a merge patch cannot change a single element. Each member of
the patch is applied like this; a patch that is not an object replaces the whole target.
```mermaid
flowchart LR
M["member of the patch"] --> N{{"value null?"}}
N -->|yes| D["delete the key"]
N -->|no| O{{"value an object?"}}
O -->|yes| R["merge recursively into the target's value"]
O -->|no| S["set the key to the value"]
```
<p class="nj-caption" markdown>How one member of a merge patch is applied: a `null` value deletes the key, an object is merged recursively, and any other value replaces the value of the key.</p>
!!! tip "Not a general deep merge"
A merge patch is not a general deep merge: a `#!json null` value in the patch deletes the key from the target.
@@ -49,6 +121,7 @@ another.
- [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations
- [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch
- Function [`merge_patch`](../api/basic_json/merge_patch.md)
- Function [`merge_diff`](../api/basic_json/merge_diff.md)
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply a JSON Merge Patch to a value
- [`merge_diff`](../api/basic_json/merge_diff.md) - create a JSON Merge Patch from two values
- Function [`update`](../api/basic_json/update.md) - merge objects, optionally recursively
- [Modifying values](modifying_values.md) - simple in-place modifications
@@ -99,10 +99,14 @@ editable document types, such as [`json_editable_document`](../api/json_editable
## See also
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
- [`push_back`](../api/basic_json/push_back.md) - / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
- [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent
- [`update`](../api/basic_json/update.md) - merge objects (shallow, or recursive with `merge_objects`)
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
- [`erase`](../api/basic_json/erase.md) - / [`clear`](../api/basic_json/clear.md) - remove elements
- [JSON Patch and Diff](json_patch.md) - and [JSON Merge Patch](merge_patch.md) - structured modifications
- [Editing a document](json_view.md#editing-a-document) - change a parsed document in place
- [`insert`](../api/basic_json/insert.md) - insert elements at an iterator position or merge a range
- [`operator+=`](../api/basic_json/operator+=.md) - shorthand for `push_back`
- [Element Access](element_access/index.md) - read elements
- [Iterators](iterators.md) - iterate over elements
+11
View File
@@ -4,6 +4,8 @@ This library has experimental support for C++ modules, introduced in C++20. The
Please be aware that the module is experimental and a full test is outstanding, and the exported symbols are subject to change.
Importing the module instead of including the header reduces the compile time of every translation unit that uses the library, at the price of building the module once. The [Compile times](../integration/compile_times.md#use-c20-modules) page has measurements and the one-off cost.
## Requirements
The `nlohmann.json` module requires that the build system is configured to build and resolve modules when imported. Obviously, as modules were introduced in C++20, this feature can only be used in C++20 and subsequent versions.
@@ -66,3 +68,12 @@ C++20 modules support is exercised in CI against current GCC and Clang on Ubuntu
- **MSVC** could fail with `C2039: 'json_sax_dom_callback_parser' is not a member of ... detail`; fixed by exporting the required internal symbols from `json.cppm` (see [Exported symbols](#exported-symbols) above). ([issue #3970](https://github.com/nlohmann/json/issues/3970))
If you hit a different module-related build failure, search [existing issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+modules) before filing a new one.
## See also
- [Supported Macros](macros.md) - macros are not exported by modules
- [Macros](../api/macros/index.md) - reference of all macros
- [`nlohmann` Namespace](namespace.md) - the library namespace
- [CMake](../integration/cmake.md) - enable the module build in CMake
- [Compile times](../integration/compile_times.md) - reduce compile times
- [`basic_json`](../api/basic_json/index.md) - the main class and all its member functions
+27
View File
@@ -38,6 +38,22 @@ For example, the namespace name for version 3.11.2 with `JSON_DIAGNOSTICS` defin
nlohmann::json_abi_diag_v3_11_2
```
The name is assembled from these segments; pick a version and macros to see the resulting name:
<figure class="nj-integration-ns" data-nj-integration-ns aria-label="How the inline namespace name is assembled from its segments">
<div class="nj-integration-ns__strip" aria-live="polite">
<span class="nj-integration-ns__seg nj-integration-ns__seg--root"><code>nlohmann</code><small>root namespace</small></span>
<span class="nj-integration-ns__seg nj-integration-ns__seg--sep"><code>::</code><small>&nbsp;</small></span>
<span class="nj-integration-ns__seg nj-integration-ns__seg--abi"><code>json_abi</code><small>always</small></span>
<span class="nj-integration-ns__seg nj-integration-ns__seg--tag"><code>_diag</code><small>JSON_DIAGNOSTICS=1</small></span>
<span class="nj-integration-ns__seg nj-integration-ns__seg--ver"><code>_v3_11_2</code><small>version 3.11.2</small></span>
</div>
<figcaption>Version 3.11.2 with <code>JSON_DIAGNOSTICS</code> defined to <code>1</code>. ABI tags are appended in the order of the
list above, the version suffix comes last.</figcaption>
</figure>
<p class="nj-caption" markdown>The name of the inline namespace, assembled from its segments. Select a version and macros to see which ABI tags appear; a tag the chosen version does not have is disabled.</p>
## Purpose
Several incompatibilities have been observed. Amongst the most common ones is linking code compiled with different
@@ -55,6 +71,8 @@ graph
app --> library
```
<p class="nj-caption" markdown>The application uses the library with `JSON_DIAGNOSTICS=1` and links `some library`, which uses the same library version with `JSON_DIAGNOSTICS=0`. Arrows point from a component to what it uses.</p>
In releases prior to 3.11.0, mixing any version of the JSON library with different `JSON_DIAGNOSTICS` settings would
result in a crashing application. If `some_library` never passes instances of JSON library types to the application,
this scenario became safe in version 3.11.0 and above due to the inline namespace yielding distinct symbol names.
@@ -102,6 +120,15 @@ follows:
Overriding the namespace and mixing ABI-incompatible versions will result in crashes or incorrect behavior. You
have been warned!
## See also
- [`NLOHMANN_JSON_NAMESPACE`](../api/macros/nlohmann_json_namespace.md) - the library namespace as macro
- [`NLOHMANN_JSON_NAMESPACE_BEGIN`, `NLOHMANN_JSON_NAMESPACE_END`](../api/macros/nlohmann_json_namespace_begin.md) - open and close the library namespace
- [`NLOHMANN_JSON_NAMESPACE_NO_VERSION`](../api/macros/nlohmann_json_namespace_no_version.md) - disable the version component
- [Supported Macros](macros.md) - all supported macros
- [Migration Guide](../integration/migration_guide.md) - do not hard-code the library namespace
- [`basic_json`](../api/basic_json/index.md) - the main class and all its member functions
## Version history
- Introduced inline namespace (`json_v3_11_0[_abi-tag]*`) in version 3.11.0.
+7 -2
View File
@@ -109,5 +109,10 @@ With exceptions off, you can replace the `abort()` by your own code, for example
- [Exceptions](../home/exceptions.md) - all exceptions and their ids
- [Parsing and exceptions](parsing/parse_exceptions.md) - the parsing alternatives in detail
- [Parsing untrusted input](parsing/untrusted_input.md)
- [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md)
- [Parsing untrusted input](parsing/untrusted_input.md) - harden an application that parses data it does not control
- [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md) - disable exceptions in the library
- [`exception`](../api/basic_json/exception.md) - base class of all exceptions thrown by the library
- [`JSON_THROW_USER`](../api/macros/json_throw_user.md) - replace the throw statements of the library
- [`parse`](../api/basic_json/parse.md) - deserialize from a compatible input
- [`accept`](../api/basic_json/accept.md) - check whether the input is valid JSON
- [Supported Macros](macros.md) - all supported macros
@@ -117,3 +117,11 @@ Assume file `input.json` contains the JSON object above:
"two": 2
}
```
## See also
- [`ordered_json`](../api/ordered_json.md) - JSON type that preserves insertion order
- [`ordered_map`](../api/ordered_map.md) - the container behind `ordered_json`
- [`object_t`](../api/basic_json/object_t.md) - type used to store JSON objects
- [Iterators](iterators.md) - iteration order of object keys
- [Element Access](element_access/index.md) - access elements by key
+11 -2
View File
@@ -13,6 +13,8 @@ flowchart LR
A --> V["bool: is the input valid JSON?"]
```
<p class="nj-caption" markdown>The three entry points for parsing: `parse()` builds a `basic_json` value, optionally filtered by a parser callback, `sax_parse()` delivers events to a SAX handler, and `accept()` only checks whether the input is valid.</p>
## Input
The [`parse`](../../api/basic_json/parse.md) function reads a JSON value from an input. The input can be
@@ -34,7 +36,7 @@ json data = json::parse(f);
```
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
is read as UTF-16 or UTF-32 -- depending on the character type -- and converted to UTF-8; see the
is read as UTF-16 or UTF-32 — depending on the character type — and converted to UTF-8; see the
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format; input
that arrives in chunks or contains several values is covered in
@@ -154,4 +156,11 @@ options.
- [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views
- [parsing and exceptions](parse_exceptions.md) - control error handling
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
- [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
- [`json_sax`](../../api/json_sax/index.md) - documentation of the SAX interface
- [`parse_error`](../../api/basic_json/parse_error.md) - exception thrown on a parse error
- [Comments](../comments.md) - ignore `//` and `/* */` comments while parsing
- [Trailing Commas](../trailing_commas.md) - accept trailing commas in arrays and objects
- [Serialization](../serialization.md) - pretty-printing, UTF-8 handling, and number output
- [Working Without Exceptions](../no_exceptions.md) - error handling when exceptions are disabled
- [Exceptions](../../home/exceptions.md) - list of exceptions and error codes
@@ -61,5 +61,12 @@ JSON Lines input with more than one value is treated as invalid JSON by the [`pa
}
```
A value that is a number must be followed by whitespace -- see the [notes](../../api/operator_gtgt.md#notes) of
A value that is a number must be followed by whitespace — see the [notes](../../api/operator_gtgt.md#notes) of
`operator>>` for details.
## See also
- [Parsing](index.md) - overview of the parsing functions
- [Large, Streamed, and Partial Input](large_and_partial_input.md) - chunked, concatenated, and very large input
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
@@ -17,6 +17,8 @@ flowchart TD
H -->|"yes"| I["stop early with SAX, or read through a view"]
```
<p class="nj-caption" markdown>Questions about the input and the approach this page describes for each; an input can match more than one question.</p>
## Several values in one stream
For newline-delimited input, read line by line and parse each line, as described in [JSON Lines](json_lines.md). For
@@ -80,6 +82,14 @@ exception is one past the buffer size in the first case:
The example feeds a document in five chunks. The first four are reported as incomplete, and a syntax error is reported as
such, not as missing data.
The chunks of the example, and the bytes each attempt parses again:
<div class="nj-fig-wrap" markdown>
--8<-- "images/chunk_buffering.svg"
</div>
<p class="nj-caption" markdown>Each row is one parse attempt on the growing buffer: an error at the byte after the end of the buffer means the value is incomplete, an error before the end is a real syntax error. The last line adds up the bytes parsed over all attempts.</p>
!!! warning "Limits of buffering"
- The buffer must contain exactly one value. Data that belongs to the next value is a syntax error. Use framing if
@@ -93,6 +103,14 @@ such, not as missing data.
The input does not have to become one huge `json` value. The two tools below process it piece by piece.
How the memory use develops while a large array of records is parsed (illustrative, not a measurement):
<div class="nj-fig-wrap" markdown>
--8<-- "images/memory_over_time.svg"
</div>
<p class="nj-caption" markdown>Memory use over the number of records parsed: `parse()` keeps the whole tree, `json_document` the text plus its index, a discarding callback one record at a time, and SAX only what the handler keeps.</p>
### Handle and discard elements with a callback
A [parser callback](parser_callbacks.md) is called for every finished value. If it returns `#!cpp false`, the value is
@@ -152,3 +170,8 @@ and gives read-only [views](../../api/basic_json_view/index.md) of the parts you
- [SAX interface](sax_interface.md) - event-based parsing
- [Zero-copy JSON views](../json_view.md) - read parts of a large document without building a tree
- [Untrusted input](untrusted_input.md) - limits for input you do not control
- [`operator>>`](../../api/operator_gtgt.md) - deserialize from stream
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
- [`JSON_PRECISE_STREAM_POSITION`](../../api/macros/json_precise_stream_position.md) - leave the stream positioned right after a parsed number
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../../api/macros/json_disable_stream_bulk_scan.md) - read a `std::istream` one character at a time
@@ -143,3 +143,14 @@ acceptable, for example for configuration files. Note that `start_pos()` is a 0-
For binary formats such as CBOR, there are no lines; `byte` is the position in the byte sequence. To find out how many
bytes a successfully parsed value used, see
[Several values in one stream](large_and_partial_input.md#several-values-in-one-stream).
## See also
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
- [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
- [`parse_error`](../../api/basic_json/parse_error.md) - exception thrown on a parse error
- [`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) - record start and end positions of parsed values
- [Working Without Exceptions](../no_exceptions.md) - error handling when exceptions are disabled
- [Exceptions](../../home/exceptions.md) - list of exceptions and error codes
- [Parsing Untrusted Input](untrusted_input.md) - limits and checks for input you do not control
@@ -31,6 +31,13 @@ table describes the values of the parameters `depth`, `event`, and `parsed`.
| `parse_event_t::array_end` | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array |
| `parse_event_t::value` | the parser finished reading a JSON value | depth of the value | the parsed JSON value |
<div class="nj-fig-wrap" markdown>
--8<-- "images/callback_events.svg"
</div>
<p class="nj-caption" markdown>Each coloured dot marks where the parser reports a `parse_event_t` event while reading
the JSON text on the left; the legend on the right names the event per colour.</p>
??? example "Example: sequence of callback events"
When parsing the following JSON text,
@@ -95,7 +102,7 @@ was called:
The JSON specification leaves the handling of objects with repeated keys up to the implementation. As described in
[`object_t`](../../api/basic_json/object_t.md#behavior), it is unspecified which value for a repeated key ends up in
the resulting `#!c json` value -- once parsing has produced that value, the duplicate is already gone, because object
the resulting `#!c json` value — once parsing has produced that value, the duplicate is already gone, because object
storage maps each key to a single value. If duplicate keys should instead be treated as an error, a parser callback
can detect them while the object is still being read, before that ambiguity ever applies.
@@ -122,7 +129,7 @@ This approach has three limitations:
maintaining a stack of the enclosing keys and array indices in the callback as well.
A [SAX interface](sax_interface.md) does not lift the position limitation: its `key` function receives no position
either -- only `parse_error` is passed the byte position.
either — only `parse_error` is passed the byte position.
## Recipe: streaming a large homogeneous array
@@ -171,3 +178,11 @@ tracking the maximum `depth` seen and throwing once it is exceeded:
json::parse(input, callback);
```
## See also
- [Parsing](index.md) - overview of the parsing functions
- [SAX Interface](sax_interface.md) - guide to event-based parsing
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
- [`parser_callback_t`](../../api/basic_json/parser_callback_t.md) - callback function type for the parser
- [`parse_event_t`](../../api/basic_json/parse_event_t.md) - enumeration of parser events
@@ -29,6 +29,8 @@ class sax_t ["json::sax_t"] {
}
```
<p class="nj-caption" markdown>The interface `json::sax_t`: one function per event, each returning `bool`; the functions marked with `*` are pure virtual.</p>
```cpp
// called when null is parsed
bool null();
@@ -62,6 +64,34 @@ bool parse_error(std::size_t position, const std::string& last_token, const json
The return value of each function determines whether parsing should proceed.
## Stepping through the events
The events for a small input, step by step, and what two different handlers do with them. Press *Play* or step with *Next*:
<div class="nj-sax nj-fig" id="sax-event-stepper" data-nj-sax role="group" aria-label="SAX events for the input {&quot;a&quot;:[1,true],&quot;b&quot;:&quot;x&quot;}, and the state of a handler after each event">
<div class="nj-sax__lane nj-sax__lane--text"><p class="nj-sax__h">input</p><p class="nj-sax__text"><code class="nj-sax__src">{"a":[1,true],"b":"x"}</code></p></div>
<div class="nj-sax__lane nj-sax__lane--events"><p class="nj-sax__h">events, in the order the parser calls them</p><ol class="nj-sax__events">
<li class="nj-sax-ev nj-sax-ev--os"><code>start_object(-1)</code></li>
<li class="nj-sax-ev nj-sax-ev--k"><code>key("a")</code></li>
<li class="nj-sax-ev nj-sax-ev--as"><code>start_array(-1)</code></li>
<li class="nj-sax-ev nj-sax-ev--v"><code>number_unsigned(1)</code></li>
<li class="nj-sax-ev nj-sax-ev--v"><code>boolean(true)</code></li>
<li class="nj-sax-ev nj-sax-ev--ae"><code>end_array()</code></li>
<li class="nj-sax-ev nj-sax-ev--k"><code>key("b")</code></li>
<li class="nj-sax-ev nj-sax-ev--v"><code>string("x")</code></li>
<li class="nj-sax-ev nj-sax-ev--oe"><code>end_object()</code></li>
</ol></div>
<div class="nj-sax__lane nj-sax__lane--state"><p class="nj-sax__h">handler: build a value tree, as <code>parse</code> does</p><div class="nj-sax__state"><pre class="nj-sax__tree"><code>{
"a": [
1,
true
],
"b": "x"
}</code></pre><p class="nj-sax__note">After <code>end_object()</code> the tree is complete and no container is open. The number of elements is passed as -1: the parser does not know it in advance.</p></div></div>
</div>
<p class="nj-caption" markdown>The three lanes show the input consumed so far, the events called so far, and the state of the selected handler (*build a tree* or *keep only /b*). Check *return false at this event* to stop the parse at the current event and see which events are no longer called.</p>
To implement your own SAX handler, proceed as follows:
1. Implement the SAX interface in a class. You can use class `nlohmann::json_sax<json>` as base class, but you can also use any class where the functions described above are implemented and public.
@@ -72,5 +102,9 @@ Note the `sax_parse` function only returns a `#!cpp bool` indicating the result
## See also
- [json_sax](../../api/json_sax/index.md) - documentation of the SAX interface
- [sax_parse](../../api/basic_json/sax_parse.md) - SAX parser
- [`json_sax`](../../api/json_sax/index.md) - documentation of the SAX interface
- [`sax_parse`](../../api/basic_json/sax_parse.md) - SAX parser
- [Parsing](index.md) - overview of the parsing functions
- [Parsing and Exceptions](parse_exceptions.md) - handling parse errors and locating them in the input
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
- [Binary Formats](../binary_formats/index.md) - CBOR, MessagePack, BSON, and other formats
@@ -1,7 +1,7 @@
# Parsing Untrusted Input
This page is for applications that parse JSON -- or one of the supported [binary formats](../binary_formats/index.md)
(BJData, BON8, BSON, CBOR, MessagePack, UBJSON) -- from a source they do not fully control, such as a network
This page is for applications that parse JSON — or one of the supported [binary formats](../binary_formats/index.md)
(BJData, BON8, BSON, CBOR, MessagePack, UBJSON) — from a source they do not fully control, such as a network
connection, an uploaded file, or another process. It summarizes what the library already does for such input and what
remains the caller's responsibility, linking to the pages that cover each aspect in detail rather than repeating them.
@@ -50,12 +50,12 @@ stack, however deeply it is nested.
[SAX handler](sax_interface.md) (for the JSON parser or a binary format alike) and throwing once your limit is
exceeded.
Once a value has been parsed, operations that walk it recursively -- serializing it with
Once a value has been parsed, operations that walk it recursively — serializing it with
[`dump`](../../api/basic_json/dump.md), hashing it, copying it, comparing two values with `#!cpp ==`, `#!cpp <`, or (in
C++20) `#!cpp <=>`, merging with [`update`](../../api/basic_json/update.md), applying a
[`merge_patch`](../../api/basic_json/merge_patch.md), creating a patch with [`diff`](../../api/basic_json/diff.md) or
[`merge_diff`](../../api/basic_json/merge_diff.md), [`flatten`](../../api/basic_json/flatten.md), and the binary
writers (`to_cbor`, `to_msgpack`, ...) -- descend at most 128 levels on the call stack and continue
writers (`to_cbor`, `to_msgpack`, ...) — descend at most 128 levels on the call stack and continue
below that with an explicit stack instead, so none of them can exhaust the stack either, however deeply the value is
nested. Destroying a value (its destructor) never recurses at all, regardless of nesting depth, for the same reason.
@@ -63,12 +63,12 @@ nested. Destroying a value (its destructor) never recurses at all, regardless of
The library does not limit the overall size of a JSON text; a value nested or wide enough will use memory
proportional to the input. If you parse untrusted input of unbounded size, check the size of the file or stream
yourself before -- or while -- handing it to `parse()`.
yourself before — or while — handing it to `parse()`.
For the binary formats, an announced size is never trusted outright:
- Reading a string or binary value copies the input in bounded 4096-byte chunks and grows the result as bytes are
actually consumed, rather than allocating the announced length up front -- a truncated input runs out of bytes
actually consumed, rather than allocating the announced length up front — a truncated input runs out of bytes
(reported as a parse error) instead of triggering an oversized allocation.
- When an array announces its number of elements and the array container supports `reserve()` (as `#!cpp std::vector`,
the default, does), the library reserves storage for at most 16384 of them upfront, regardless of how large the
@@ -91,7 +91,7 @@ Invalid UTF-8 is rejected while parsing, not just while serializing:
A `#!cpp '\0'` (NUL) byte *inside* a quoted JSON string is always rejected (it must be escaped as `\u0000`). A NUL byte
*outside* of a string is different: by default it is silently treated as the end of the input, so trailing bytes after
it -- including further, otherwise well-formed JSON -- are silently ignored rather than rejected. Since untrusted input
it — including further, otherwise well-formed JSON — are silently ignored rather than rejected. Since untrusted input
that happens to embed a NUL is a way to make part of it disappear without a parse error, see the
[FAQ entry](../../home/faq.md#nul-bytes-in-the-input) and consider defining
[`JSON_STRICT_NUL_HANDLING`](../../api/macros/json_strict_nul_handling.md) to `1` to reject a NUL byte like any other
@@ -116,7 +116,7 @@ silently resolving them one way or another, see the
## Numbers
A number whose value cannot be represented -- for instance `1E1000`, which overflows `double` -- is rejected while
A number whose value cannot be represented — for instance `1E1000`, which overflows `double` — is rejected while
parsing as [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) rather than silently becoming
infinity. An integer that is syntactically valid but does not fit the 64-bit integer types is not rejected; it is
instead stored as a `double`, which may lose precision for very large values. See
@@ -139,7 +139,7 @@ conforming JSON consumers is exactly what the default rejects.
[SAX handler](sax_interface.md); the library bounds the call stack but not memory use.
- Bound the size of the input itself before parsing, independent of the library's own bounded allocations for binary
format lengths.
- Decide up front how you want a string with invalid UTF-8 -- from any source, not only parsing -- to be serialized
- Decide up front how you want a string with invalid UTF-8 — from any source, not only parsing — to be serialized
(`strict`, `replace`, or `ignore`), rather than discovering it from an uncaught `type_error.316`.
- If a stray NUL byte silently truncating trailing input is a problem for your input format, define
`JSON_STRICT_NUL_HANDLING`.
@@ -160,3 +160,9 @@ report a security issue in the library itself, follow the [security policy](../.
- [Number handling](../types/number_handling.md) - number ranges and overflow behavior
- [Assurance case](../../community/assurance_case.md) - the library's threat model and countermeasures
- [Security policy](../../community/security_policy.md) - how to report a vulnerability
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
- [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
- [`parser_callback_t`](../../api/basic_json/parser_callback_t.md) - callback function type for the parser
- [`JSON_STRICT_NUL_HANDLING`](../../api/macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
- [`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) - record start and end positions of parsed values
+42 -18
View File
@@ -6,7 +6,7 @@ first. If a hard real-time budget or the last percent of throughput matters more
[faster, more specialized library](https://github.com/miloyip/nativejson-benchmark#parsing-time) may be a better fit.
That said, how you use this library still makes a measurable difference. This page collects practical, code-verified
techniques for reducing time, memory, and compile-time cost -- without repeating the detailed pages it links to.
techniques for reducing time, memory, and compile-time cost — without repeating the detailed pages it links to.
## Parsing input
@@ -14,8 +14,8 @@ techniques for reducing time, memory, and compile-time cost -- without repeating
`#!cpp FILE*` (see [Parsing](parsing/index.md#input)). Internally, every input is wrapped in an
[input adapter](../home/architecture.md#input-adapters), and not all adapters are equally fast.
For inputs backed by contiguous, single-byte memory -- a `#!cpp std::string`, a `#!cpp std::vector<char>`, a string
literal, or a pointer range -- the library uses `iterator_input_adapter`, wrapped in a raw pointer so the fast paths
For inputs backed by contiguous, single-byte memory — a `#!cpp std::string`, a `#!cpp std::vector<char>`, a string
literal, or a pointer range — the library uses `iterator_input_adapter`, wrapped in a raw pointer so the fast paths
below apply on every supported standard. This adapter exposes two optimizations the lexer detects at compile time:
- it can reconstruct already-consumed input on demand for error messages, instead of copying every character as it is
@@ -69,7 +69,7 @@ a DOM, three alternatives avoid building it:
`basic_json` values.
- Pass a [parser callback](parsing/parser_callbacks.md) to `parse`. This still builds a DOM, but the callback can
discard finished elements as soon as they are handled, so memory usage stays bounded by one element (plus the
unparsed remainder of the input) instead of the whole document -- see the [recipe for streaming a large homogeneous
unparsed remainder of the input) instead of the whole document — see the [recipe for streaming a large homogeneous
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
- Parse into a [`json_document`](json_view.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
the input text and builds a flat index of 16 bytes per value; strings and floating-point numbers are not copied, but
@@ -85,35 +85,53 @@ at a time, and a malformed line does not invalidate lines already processed.
## Binary formats
JSON text is not a compact format. If the data is only exchanged between programs (not read by humans), the
[binary formats](binary_formats/index.md) -- BJData, BON8, BSON, CBOR, MessagePack, and UBJSON -- encode the same
[binary formats](binary_formats/index.md) — BJData, BON8, BSON, CBOR, MessagePack, and UBJSON — encode the same
values more compactly, which reduces both the bytes transferred and, for most of them, the work needed to parse them
back. The [size comparison](binary_formats/index.md#sizes) on that page, measured against minified JSON for four
reference documents, shows the effect varies a lot by document shape: CBOR and MessagePack come out at 50.5% of the
minified JSON size for the numeric-array-heavy `canada.json`, but only around 87-88% for the string-heavy
`jeopardy.json`, where there is less numeric data to encode more compactly. BON8 is the most compact option in that
comparison for text-heavy documents (63.5%-87.5%), at the cost of an
[incomplete serializer](binary_formats/index.md#completeness) (no unsigned integers above int64). Which format -- and
whether it is worth the loss of human readability at all -- depends on the actual data; see the
[incomplete serializer](binary_formats/index.md#completeness) (no unsigned integers above int64). Which format — and
whether it is worth the loss of human readability at all — depends on the actual data; see the
[comparison tables](binary_formats/index.md#comparison) before choosing one.
<div class="nj-charts-grid-wrap" markdown>
<div class="nj-fig-wrap" markdown>
--8<-- "images/binary_sizes_canada.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/binary_sizes_jeopardy.svg"
</div>
</div>
<p class="nj-charts-caption" markdown>Two of the four panels from the [size comparison](binary_formats/index.md#sizes);
the smallest format per file is drawn in a stronger colour than the others.</p>
## Object type: `json` vs. `ordered_json`
The default [`json`](../api/json.md) type stores object keys in a `#!cpp std::map`, giving logarithmic-time lookup,
insertion, and erasure, at the cost of sorting keys alphabetically rather than preserving insertion order (see
[Object Order](object_order.md)). [`ordered_json`](../api/ordered_json.md) uses
[`nlohmann::ordered_map`](../api/ordered_map.md) instead, a `#!cpp std::vector`-backed container with no lookup index:
every key-based operation is a **linear scan**, so building an object of `n` distinct keys costs **O(n²)** in total --
every key-based operation is a **linear scan**, so building an object of `n` distinct keys costs **O(n²)** in total —
this applies equally to inserting keys one by one and to parsing an object, since the parser inserts each key as it is
read. The [measurements on the `ordered_map` page](../api/ordered_map.md#complexity) show this is
negligible at typical object sizes (2000 keys: 0.7 ms for `json` vs. 3.6 ms for `ordered_json`, a 5x factor) but grows
steeply for large, machine-generated objects (16 000 keys: 3.3 ms vs. 181.6 ms, a 54x factor).
<div class="nj-fig-wrap" markdown>
--8<-- "images/ordered_json_scaling.svg"
</div>
<p class="nj-charts-caption" markdown>Parsing a flat object of `n` keys, measured with `-O2 -DNDEBUG`; all
four sizes from the [table on the `ordered_map` page](../api/ordered_map.md#complexity), both
axes logarithmic.</p>
If insertion order matters *and* an object routinely has many thousands of keys, `ordered_json`'s quadratic build cost
may not be acceptable. The library's [`ObjectType` template parameter](types/template_parameters.md#objecttype) can be
set to a different container instead: `#!cpp nlohmann::fifo_map` keeps insertion order with a real lookup index
(avoiding the quadratic cost), while `#!cpp std::unordered_map`, `#!cpp boost::unordered_flat_map`,
`#!cpp absl::flat_hash_map`, and similar hash maps trade insertion order for average-case constant-time lookup (through
an adapter, since their template argument order does not match what `basic_json` expects) -- see
an adapter, since their template argument order does not match what `basic_json` expects) — see
[Object Order](object_order.md#alternative-behavior-preserve-insertion-order) for the full list.
## Avoiding copies
@@ -128,13 +146,13 @@ an adapter, since their template argument order does not match what `basic_json`
- **Access without copying.** [`get<T>()`](../api/basic_json/get.md) returns a copy of the stored value converted to
`T`. When a reference or pointer to the value already stored inside the `basic_json` is enough,
[`get_ref()`](../api/basic_json/get_ref.md) and [`get_ptr()`](../api/basic_json/get_ptr.md) access it directly:
both pages state, word for word, "No copies are made." -- at the cost of that reference or pointer becoming invalid
both pages state, word for word, "No copies are made." — at the cost of that reference or pointer becoming invalid
once the underlying value changes.
- **Iterate by reference.** `#!cpp basic_json::iterator::operator*()` returns a `reference` (an alias for
`#!cpp basic_json&`), but a range-based for loop with a by-value loop variable (`#!cpp for (auto el : j)`) still
copies each element, because plain `#!cpp auto` drops the reference. Write `#!cpp for (const auto& el : j)` (or
`#!cpp auto&` for a mutable loop), and use [`items()`](../api/basic_json/items.md) the same way when the key is
needed too -- its own examples use `#!cpp for (auto& el : j.items())`.
needed too — its own examples use `#!cpp for (auto& el : j.items())`.
- **Construct in place.** [`emplace_back()`](../api/basic_json/emplace_back.md) (arrays, amortized constant time) and
[`emplace()`](../api/basic_json/emplace.md) (objects, logarithmic in the size of the container for `json`) forward
their arguments directly to a `basic_json` constructor, rather than requiring a temporary value to be
@@ -143,13 +161,13 @@ an adapter, since their template argument order does not match what `basic_json`
in instead of copying it.
- **Skip the bounds check when it is redundant.** [`at()`](../api/basic_json/at.md) and
[`operator[]`](../api/basic_json/operator%5B%5D.md) have the same complexity (constant for a valid array index,
logarithmic for an object key in `json`) -- the difference is that `at()` additionally checks the key or index and
logarithmic for an object key in `json`) — the difference is that `at()` additionally checks the key or index and
throws if it is invalid, while `operator[]` does not (see [unchecked access](element_access/unchecked_access.md) and
[checked access](element_access/checked_access.md)). Prefer `operator[]` when the surrounding code has already
established that the access is valid.
- **Reserve array capacity.** `basic_json` has no public `reserve()`, but when building a large array
incrementally with a known final size, [`get_ref()`](../api/basic_json/get_ref.md) exposes the underlying
`#!cpp array_t` so it can be reserved directly -- see
`#!cpp array_t` so it can be reserved directly — see
["reserving array capacity"](element_access/unchecked_access.md#performance-reserving-array-capacity) for the
one-line recipe.
@@ -182,7 +200,7 @@ an adapter, since their template argument order does not match what `basic_json`
(word for word from the page); any non-negative `indent` pretty-prints instead, which is more readable but produces
more bytes and more work. `dump()` builds and returns a complete `#!cpp string_t` containing the whole serialization.
[`operator<<`](../api/operator_ltlt.md) writes directly to a `#!cpp std::ostream` instead, through the same
serializer, but without ever materializing that intermediate string -- so if the destination is a stream (a file, or
serializer, but without ever materializing that intermediate string — so if the destination is a stream (a file, or
`#!cpp std::cout`), `#!cpp os << j;` avoids the allocation and copy that `#!cpp os << j.dump();` would incur for large
values.
@@ -193,7 +211,7 @@ while it is in use:
- [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) adds a JSON Pointer to exception messages, pointing at the
value that triggered the exception. Quoting the page directly: "enabling this macro increases the size of every
JSON value by one pointer and adds some runtime overhead" -- every value gains a parent pointer that has to be kept
JSON value by one pointer and adds some runtime overhead" — every value gains a parent pointer that has to be kept
up to date as the document is built and modified.
- [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) adds
[`start_pos()`](../api/basic_json/start_pos.md) and [`end_pos()`](../api/basic_json/end_pos.md), the byte offsets a
@@ -210,7 +228,7 @@ untrusted input, or in a debug build), and keep them off in a release build that
[`basic_json`](../api/basic_json/index.md), [`json`](../api/json.md), [`ordered_json`](../api/ordered_json.md),
[`json_pointer`](../api/json_pointer/index.md), and [`adl_serializer`](../api/adl_serializer/index.md), pulling in only
a handful of lightweight standard headers instead of the full `json.hpp`. A header that only needs to *name*
`nlohmann::json` -- in a function signature or a class member declaration, for instance -- can include `json_fwd.hpp`
`nlohmann::json` — in a function signature or a class member declaration, for instance — can include `json_fwd.hpp`
and leave `#!cpp #include <nlohmann/json.hpp>` to the source files that actually parse, build, or serialize values,
the same way a project would forward-declare any other heavy class to keep it out of widely-included headers:
@@ -231,8 +249,8 @@ nlohmann::json my_type::config() const { /* ... */ }
```
One caveat: ABI-affecting macros such as `JSON_DIAGNOSTICS` and `JSON_DIAGNOSTIC_POSITIONS` are encoded into the
library's [inline namespace name](namespace.md#limitations). Every translation unit -- whether it includes
`json_fwd.hpp` or the full header -- must define them the same way, or linking fails with undefined references
library's [inline namespace name](namespace.md#limitations). Every translation unit — whether it includes
`json_fwd.hpp` or the full header — must define them the same way, or linking fails with undefined references
instead of a compile error.
If I/O support is not needed at all, [`JSON_NO_IO`](../api/macros/json_no_io.md) excludes `<cstdio>`, `<ios>`,
@@ -252,3 +270,9 @@ those headers are then never processed by the compiler at all.
- [Object Order](object_order.md) - `json` vs. `ordered_json` and other `ObjectType` choices
- [Template Parameter Requirements](types/template_parameters.md) - custom container and allocator types
- [Supported macros](macros.md) - overview of all configuration macros, including the diagnostics ones above
- [`JSON_NO_SIMD`](../api/macros/json_no_simd.md) - disable SIMD acceleration
- [`JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md) - use simdutf for UTF-8 validation
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md) - disable the stream bulk scan
- [`parse`](../api/basic_json/parse.md) - deserialize from a compatible input
- [`sax_parse`](../api/basic_json/sax_parse.md) - parse with a SAX interface
- [`json_view`](../api/json_view.md) - a non-owning view of a value in a document
@@ -128,3 +128,7 @@ Besides JSON text, a value can also be serialized to the more compact [binary fo
- [`std::formatter<basic_json>`](../api/basic_json/std_formatter.md) - use JSON values with `std::format` and `std::print`
- [`format_as`](../api/basic_json/format_as.md) - use JSON values with the {fmt} library
- [Parsing](parsing/index.md) - the reverse operation
- [`error_handler_t`](../api/basic_json/error_handler_t.md) - how to treat invalid UTF-8 when dumping
- [`basic_json_view::dump`](../api/basic_json_view/dump.md) - serialize a view directly
- [`JSON_NO_IO`](../api/macros/json_no_io.md) - remove stream and file I/O
- [`JSON_NO_SIMD`](../api/macros/json_no_simd.md) - disable SIMD acceleration
@@ -37,3 +37,10 @@ For more information, see [JSON With Commas and Comments (JWCC)](https://nigelta
```
--8<-- "examples/trailing_commas.output"
```
## See also
- [`parse`](../api/basic_json/parse.md) - deserialize from a compatible input
- [Comments](comments.md) - ignore `//` and `/* */` comments while parsing
- [Parsing](parsing/index.md) - overview of the parsing functions
- [Parsing Untrusted Input](parsing/untrusted_input.md) - limits and checks for input you do not control
+6 -1
View File
@@ -120,5 +120,10 @@ same two functions.
## See also
- [Arbitrary type conversions](arbitrary_types.md) - the conversion mechanism and macros
- [Why isn't my `to_json` found?](arbitrary_types.md#why-isnt-my-to_json-or-from_json-found)
- [Why isn't my `to_json` found?](arbitrary_types.md#why-isnt-my-to_json-or-from_json-found) - troubleshoot conversion
functions that are not picked up
- [Converting values](conversions.md) - `get`, implicit conversions, and `std::optional`
- [`NLOHMANN_DEFINE_DERIVED_TYPE`](../api/macros/nlohmann_define_derived_type.md) - conversion functions for derived classes
- [`adl_serializer`](../api/adl_serializer/index.md) - customize conversions for third-party types
- [`get`](../api/basic_json/get.md) - convert a JSON value to a C++ type
+15 -5
View File
@@ -64,6 +64,8 @@ basic_json .. json_value
basic_json .. value_t
```
<p class="nj-caption" markdown>Storage of a `basic_json` value: the enumeration `value_t` holds the type, and the union `json_value` holds the value, as a pointer for objects, arrays, strings, and binary values and directly for booleans and numbers.</p>
## Template arguments
The data types to store a JSON value are derived from the template arguments passed to class `basic_json`:
@@ -290,8 +292,16 @@ Integer number values, unsigned integer number values, and floating-point number
## See also
- [Number handling](number_handling.md) - how numbers are stored and converted
- [Template parameter requirements](template_parameters.md)
- [**basic_json**](../../api/basic_json/index.md) - the class template and its member types
- [**type**](../../api/basic_json/type.md) and [**value_t**](../../api/basic_json/value_t.md) - query the type of a value
- [**object_t**](../../api/basic_json/object_t.md), [**array_t**](../../api/basic_json/array_t.md), [**string_t**](../../api/basic_json/string_t.md), [**number_integer_t**](../../api/basic_json/number_integer_t.md), [**number_float_t**](../../api/basic_json/number_float_t.md) - the underlying types
- [Object order](../object_order.md)
- [Template parameter requirements](template_parameters.md) - what the template parameters of `basic_json` must provide
- [`basic_json`](../../api/basic_json/index.md) - the class template and its member types
- [`type`](../../api/basic_json/type.md) - and [`value_t`](../../api/basic_json/value_t.md) - query the type of a value
- [`object_t`](../../api/basic_json/object_t.md), [`array_t`](../../api/basic_json/array_t.md), [`string_t`](../../api/basic_json/string_t.md), [`number_integer_t`](../../api/basic_json/number_integer_t.md), [`number_float_t`](../../api/basic_json/number_float_t.md) - the underlying types
- [Object order](../object_order.md) - how object keys are ordered
- [`json`](../../api/json.md) - the default JSON type
- [`boolean_t`](../../api/basic_json/boolean_t.md) - the underlying boolean type
- [`number_unsigned_t`](../../api/basic_json/number_unsigned_t.md) - the underlying unsigned integer type
- [`is_primitive`](../../api/basic_json/is_primitive.md) - and the other `is_*` functions query the type
- [`is_structured`](../../api/basic_json/is_structured.md) - true for objects and arrays
- [Converting values](../conversions.md) - convert JSON values to and from C++ types
- [Creating JSON values](../creating_values.md) - build JSON values
@@ -27,7 +27,11 @@ JSON defines the syntax of numbers as follows:
The following railroad diagram from [json.org](https://json.org) visualizes the number syntax:
![Syntax for JSON numbers](../../images/json_syntax_number.png)
<div class="railroad-wrap" markdown>
--8<-- "images/json_syntax_number.svg"
</div>
<p class="nj-caption" markdown>Read the diagram from left to right: every path along the lines spells a valid number, made of an optional minus sign, an integer part, an optional fraction, and an optional exponent.</p>
### Number interoperability
@@ -69,15 +73,17 @@ otherwise, it uses unsigned integer storage.
```mermaid
flowchart TD
A["number literal"] --> B{"has a fraction (.) or exponent (e/E)?"}
A["number literal"] --> B{{"has a fraction (.) or exponent (e/E)?"}}
B -->|"yes"| F["number_float_t"]
B -->|"no"| C{"has a leading minus sign?"}
B -->|"no"| C{{"has a leading minus sign?"}}
C -->|"yes"| D["try number_integer_t"]
C -->|"no"| E["try number_unsigned_t"]
D -->|"overflow"| F
E -->|"overflow"| F
```
<p class="nj-caption" markdown>How the parser picks the storage type of a number literal: a fraction or exponent always gives `number_float_t`; otherwise the sign selects the signed or unsigned integer type, and a value that overflows it is stored as `number_float_t`.</p>
!!! info "Notes"
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
@@ -131,6 +137,32 @@ The JSON number grammar allows for different ways to express zero, and this libr
That is, `-0` is stored as a signed integer, but the serialization does not reproduce the `-`.
The literals mentioned on this page, parsed with `json::parse` and serialized with `dump()` (checked against the
library; pick one or type it to see the row):
<div class="nj-charts-num" markdown>
| Literal | Stored as (`type()`) | `dump()` |
|---------|----------------------|----------|
| `0` | `number_unsigned` (`std::uint64_t`) | `0` |
| `-0` | `number_integer` (`std::int64_t`) | `0` |
| `-0.0` | `number_float` (`double`) | `-0.0` |
| `1E3` | `number_float` (`double`) | `1000.0` |
| `-12345678912345789123456789` | `number_float` (`double`) | `-1.2345678912345788e+25` |
| `18446744073709551615` | `number_unsigned` (`std::uint64_t`) | `18446744073709551615` |
| `18446744073709551616` | `number_float` (`double`) | `1.8446744073709552e+19` |
| `-9223372036854775808` | `number_integer` (`std::int64_t`) | `-9223372036854775808` |
| `1E400` | exception `out_of_range.406` | (nothing, parsing fails) |
| `1E-400` | `number_float` (`double`) | `0.0` |
| `3.141592653589793238462643383279` | `number_float` (`double`) | `3.141592653589793` |
| `2555.5599999999999` | `number_float` (`double`) | `2555.56` |
| `0.0000972439793401814` | `number_float` (`double`) | `9.72439793401814e-05` |
| `12345E-5` | `number_float` (`double`) | `0.12345` |
</div>
<p class="nj-caption" markdown>Each row shows how a literal is stored and how `dump()` writes it back. The explorer only knows the literals in this list; other input is reported as not in the verified list.</p>
### Number serialization
- Integer numbers are serialized as is; that is, no scientific notation is used.
@@ -329,6 +361,8 @@ the stored number:
| [`type_name()`](../../api/basic_json/type_name.md) | `#!c "number"` | `#!c "number"` | `#!c "number"` | `#!c "string"` |
| [`type()`](../../api/basic_json/type.md) | `number_unsigned` | `number_integer` | `number_float` | `string` |
<p class="nj-table-caption" markdown>Return value of each function for a value that stores an unsigned integer, a signed integer, or a floating-point number, and for a string for comparison.</p>
### Template number types
The number types can be changed with template parameters.
@@ -362,3 +396,12 @@ The number types can be changed with template parameters.
Note values should then be parsed with `json_ld::parse` rather than `json::parse` as the latter would parse
floating-point values to `#!c double` before then converting them to `#!c long double`.
## See also
- [Types](index.md) - how all value types are stored
- [Converting values](../conversions.md) - convert numbers to C++ types
- [`number_integer_t`](../../api/basic_json/number_integer_t.md) - the signed integer type
- [`number_unsigned_t`](../../api/basic_json/number_unsigned_t.md) - the unsigned integer type
- [`number_float_t`](../../api/basic_json/number_float_t.md) - the floating-point type
- [`is_number`](../../api/basic_json/is_number.md) - check for a number of any kind
@@ -1,7 +1,7 @@
# Template Parameter Requirements
Class [`basic_json`](../../api/basic_json/index.md) is configurable through eleven template parameters. The library
never formally states what a type passed for one of these parameters has to provide -- the requirements are implied by
never formally states what a type passed for one of these parameters has to provide — the requirements are implied by
the way the library uses the resulting [`object_t`](../../api/basic_json/object_t.md),
[`array_t`](../../api/basic_json/array_t.md), [`string_t`](../../api/basic_json/string_t.md), etc. This page collects
these requirements so they do not have to be discovered by trial and error. Each section lists the concrete types
@@ -16,9 +16,9 @@ To change a single template parameter and keep the others, use the member alias
Requirements are split into two groups:
- **Always required** -- needed to instantiate `basic_json` at all, or needed by functions that virtually every program
- **Always required** — needed to instantiate `basic_json` at all, or needed by functions that virtually every program
uses (construction, element access, [`dump`](../../api/basic_json/dump.md)).
- **Required for ...** -- only needed when a particular part of the API is instantiated. Member function templates are
- **Required for ...** — only needed when a particular part of the API is instantiated. Member function templates are
only instantiated when they are used, so a type may be perfectly usable even though it does not satisfy these
requirements, as long as the corresponding functions are never called.
@@ -56,8 +56,8 @@ Requirements are split into two groups:
!!! warning "Third-party containers and incomplete types"
`object_t` is instantiated inside the definition of `basic_json` -- it is probed for a `key_compare` member to
form [`object_comparator_t`](../../api/basic_json/object_comparator_t.md) -- i.e. while `basic_json` is still an
`object_t` is instantiated inside the definition of `basic_json` — it is probed for a `key_compare` member to
form [`object_comparator_t`](../../api/basic_json/object_comparator_t.md) — i.e. while `basic_json` is still an
incomplete type. `#!cpp std::map` is required by the standard to support incomplete mapped types; most
third-party maps are not, and inspecting the mapped type at class scope (for instance with
`#!cpp std::is_trivially_move_assignable`) makes them unusable as `ObjectType`, no matter how their template
@@ -65,7 +65,7 @@ Requirements are split into two groups:
`robin_hood::unordered_node_map`, `folly::F14FastMap`, and `eastl::hash_map`.
`array_t` is only *named* in the class definition and is not instantiated until `basic_json` is complete, so an
`ArrayType` that inspects its value type at class scope is generally fine -- `boost::container::small_vector` and
`ArrayType` that inspects its value type at class scope is generally fine — `boost::container::small_vector` and
`static_vector` both reject incomplete value types yet work here. `absl::InlinedVector` is the exception: the
`#!cpp std::is_trivially_move_assignable<basic_json>` it evaluates while instantiating itself re-enters the
library's own trait machinery mid-instantiation.
@@ -93,7 +93,7 @@ i.e., the template arguments follow the order and meaning of `std::map`.
- The template must be usable with **four** type arguments in the order shown above. The third argument is a
**comparator**; containers that expect something else in this position (e.g., a hash function) need an alias template
or wrapper -- see [Notes](#notes).
or wrapper — see [Notes](#notes).
- An optional member type `key_compare`. If it is present it becomes
[`object_comparator_t`](../../api/basic_json/object_comparator_t.md); otherwise
[`default_object_comparator_t`](../../api/basic_json/default_object_comparator_t.md) is used.
@@ -116,7 +116,7 @@ i.e., the template arguments follow the order and meaning of `std::map`.
semantics; multimaps cannot be used.
- The type must be swappable (via `std::swap` or an ADL `swap`).
- The comparison operators `==` and `<`; `!=`, `<=`, `>`, and `>=` are derived from them. Where the library uses
three-way comparison (C++20), `==` and `<=>` are required **instead** -- the six two-way operators do not satisfy
three-way comparison (C++20), `==` and `<=>` are required **instead** — the six two-way operators do not satisfy
it. They implement [`basic_json`'s comparison operators](../../api/basic_json/operator_eq.md).
### Required for heterogeneous key lookup
@@ -200,7 +200,7 @@ object keys are serialized by [`dump`](../../api/basic_json/dump.md) and visited
With [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md) enabled, the library detects insertion-ordered maps by
probing for a `capacity()` member function (`nlohmann::ordered_map` inherits it from `std::vector`) and refreshes all
parent pointers after every insertion. An `ObjectType` that happens to have a `capacity()` member is therefore treated
conservatively -- this is correct, but slower.
conservatively — this is correct, but slower.
#### Key order and duplicate keys
@@ -211,7 +211,7 @@ The library does not sort or de-duplicate keys itself; the behavior described in
`docs/mkdocs/docs/examples/custom_object_type.hpp` wraps a private `#!cpp std::map` and satisfies every
requirement above. It does not define `key_compare`, so `object_comparator_t` falls back to
[`default_object_comparator_t`](../../api/basic_json/default_object_comparator_t.md) -- a good starting point for
[`default_object_comparator_t`](../../api/basic_json/default_object_comparator_t.md) — a good starting point for
a custom `ObjectType`.
```cpp
@@ -305,7 +305,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
!!! tip "Reference implementation"
`docs/mkdocs/docs/examples/custom_array_type.hpp` wraps a private `#!cpp std::vector` and satisfies every
requirement above -- a good starting point for a custom `ArrayType`.
requirement above — a good starting point for a custom `ArrayType`.
```cpp
--8<-- "examples/custom_array_type.hpp"
@@ -384,13 +384,13 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
### Required for JSON Pointer, `flatten`, and `diff`
- A static member `npos` and the member function `find_first_of(char, size_type)` -- together with `data()`,
- A static member `npos` and the member function `find_first_of(char, size_type)` — together with `data()`,
`reserve(n)`, and `append(const char*, size_type)` they implement the escaping and unescaping of reference tokens
described in RFC 6901. Neither `find(const StringType&, size_type)`, nor `substr(pos, count)`, nor
`replace(pos, count, const StringType&)` is required.
- `empty()`.
- `resize(n)` (one argument) -- used by `to_string` to size the result before the reference tokens are copied into it.
- `begin()` and `end()` -- used by
- `resize(n)` (one argument) — used by `to_string` to size the result before the reference tokens are copied into it.
- `begin()` and `end()` — used by
[`operator[](const json_pointer&)`](../../api/basic_json/operator%5B%5D.md) to decide whether a reference token
denotes an array index.
@@ -416,7 +416,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
| `#!cpp std::pmr::string` | see the warning below before relying on the memory resource |
| `boost::container::string` | needs a user-supplied `#!cpp std::hash` specialization (Boost provides `boost::hash` instead) |
| `folly::fbstring` | requires C++20, see the note above |
| `eastl::string` | needs a user-supplied `#!cpp std::hash` and an ADL `int_to_string` (it is not assignable from a `#!cpp std::string`); [`parse`](../../api/basic_json/parse.md) does not accept it directly -- pass a character range or a `#!cpp std::string` |
| `eastl::string` | needs a user-supplied `#!cpp std::hash` and an ADL `int_to_string` (it is not assignable from a `#!cpp std::string`); [`parse`](../../api/basic_json/parse.md) does not accept it directly — pass a character range or a `#!cpp std::string` |
| a custom string class in a user-defined namespace | if the requirements above are met |
### Types that cannot be used
@@ -434,13 +434,13 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
allocation and has to be stateless (see [`AllocatorType`](#allocatortype)), and string values the library creates
are constructed with their own default allocator. So:
- Every string the library itself produces -- from [`parse`](../../api/basic_json/parse.md), from
[`dump`](../../api/basic_json/dump.md), or by default construction -- allocates from
- Every string the library itself produces — from [`parse`](../../api/basic_json/parse.md), from
[`dump`](../../api/basic_json/dump.md), or by default construction — allocates from
`#!cpp std::pmr::get_default_resource()`.
- **Copying** an arena-backed string into a value silently drops its memory resource: the copy lands on the
default resource, because `#!cpp std::pmr::polymorphic_allocator` does not propagate on copy construction.
Nothing warns about this.
- **Moving** one in does keep it, and later growth still allocates from that arena -- but it does not survive a
- **Moving** one in does keep it, and later growth still allocates from that arena — but it does not survive a
copy of the enclosing `basic_json`.
- Passing `#!cpp std::pmr::polymorphic_allocator` as `AllocatorType` does not work around any of this; it does
not compile.
@@ -451,7 +451,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
!!! tip "Reference implementation"
`docs/mkdocs/docs/examples/custom_string_type.hpp` wraps a private `#!cpp std::string` and satisfies every
requirement above -- a good starting point for a custom `StringType`. The unit test
requirement above — a good starting point for a custom `StringType`. The unit test
`tests/src/unit-alt-string.cpp` contains a more thorough variant, `alt_string`, exercised against a larger part
of the API.
@@ -479,8 +479,8 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
- A literal type that is trivially default-constructible, trivially copyable, and trivially destructible; otherwise the
union's special member functions are deleted.
- **Implicitly** convertible from `#!cpp bool` -- an `#!cpp explicit` constructor is not enough, because the
`to_json` overload for a custom `BooleanType` is constrained on `#!cpp std::is_convertible` -- and contextually
- **Implicitly** convertible from `#!cpp bool` — an `#!cpp explicit` constructor is not enough, because the
`to_json` overload for a custom `BooleanType` is constrained on `#!cpp std::is_convertible` — and contextually
convertible to `#!cpp bool` (here an `#!cpp explicit operator bool` is fine).
- Comparison operators `==`, `!=`, `<`, `<=`, `>`, `>=` (or `<=>`).
- Convertible from and to `#!cpp bool` through the serializer, because
@@ -491,7 +491,7 @@ There is little reason to use anything other than `#!cpp bool` here.
### Compatible types
`#!cpp bool` is the only usable choice. Another trivially copyable type that is implicitly convertible to and from
`#!cpp bool` -- `#!cpp std::uint8_t`, say -- does compile, and JSON booleans still round-trip, but the type then
`#!cpp bool` — `#!cpp std::uint8_t`, say — does compile, and JSON booleans still round-trip, but the type then
serves as both `boolean_t` and an ordinary integer: `basic_json` can no longer be constructed or assigned from a
`#!cpp std::uint8_t` at all (the boolean and unsigned-integer `to_json` overloads become ambiguous), and
[`get<std::uint8_t>()`](../../api/basic_json/get.md) on a number throws
@@ -505,7 +505,7 @@ Both types are stored **directly** inside `basic_json`'s union.
- `#!cpp std::is_integral` must be satisfied: `NumberIntegerType` must be a **signed** integer type,
`NumberUnsignedType` an **unsigned** integer type or the same type as `NumberIntegerType` (see
[below](#same-type-for-both)). Class types are not supported -- among others, the constructors taking integer values
[below](#same-type-for-both)). Class types are not supported — among others, the constructors taking integer values
are constrained on `#!cpp std::is_integral`.
- Trivially default-constructible, trivially copyable, and trivially destructible (union member).
- `#!cpp std::numeric_limits` must be specialized for both types.
@@ -635,7 +635,7 @@ process deeply nested values without recursion.
### Always required
- The template must accept **two** type arguments. It does not have to give the second one a default -- `basic_json`
- The template must accept **two** type arguments. It does not have to give the second one a default — `basic_json`
declares the parameter as `#!cpp template<typename T, typename SFINAE = void> class JSONSerializer`, so uses such as
`#!cpp JSONSerializer<T>` inside the library supply `#!cpp void` themselves. The second parameter exists so that
partial specializations can be constrained by SFINAE.
@@ -668,7 +668,7 @@ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
### Always required
- A non-`final` class type -- [`byte_container_with_subtype`](../../api/byte_container_with_subtype/index.md) derives
- A non-`final` class type — [`byte_container_with_subtype`](../../api/byte_container_with_subtype/index.md) derives
from it publicly.
- A member type `value_type` that is **exactly one byte** wide (e.g., `#!cpp std::uint8_t`, `#!cpp char`, or
`#!cpp std::byte`). Readers and writers reinterpret the container's storage as raw bytes, so a wider `value_type` is
@@ -695,7 +695,7 @@ such a container to a `basic_json` value.
!!! tip "Reference implementation"
`docs/mkdocs/docs/examples/custom_binary_type.hpp` wraps a private `#!cpp std::vector<std::uint8_t>` and satisfies
every requirement above -- a good starting point for a custom `BinaryType`.
every requirement above — a good starting point for a custom `BinaryType`.
```cpp
--8<-- "examples/custom_binary_type.hpp"
@@ -731,7 +731,7 @@ such a container to a `basic_json` value.
| `QByteArray` | no `empty()` (it spells that `isEmpty()`); its `insert` takes an index rather than an iterator; and it converts to `string_t`, which makes `to_json` ambiguous between a string and a binary value |
| `#!cpp std::string` | `binary_t::container_type` and `string_t` would be the same type, so the two [`swap`](../../api/basic_json/swap.md) overloads collide and `basic_json` cannot be instantiated at all |
| `#!cpp std::deque<std::uint8_t>` | storage is not contiguous, so there is no `data()` |
| containers whose `value_type` is wider than one byte | see above -- accepted by the compiler, wrong at runtime |
| containers whose `value_type` is wider than one byte | see above — accepted by the compiler, wrong at runtime |
## `CustomBaseClass`
@@ -778,7 +778,15 @@ See [issue #3425](https://github.com/nlohmann/json/issues/3425), [`string_t`](..
## See also
- [Types](index.md) -- overview of how JSON values are stored
- [Number Handling](number_handling.md) -- how the number types affect parsing and serialization
- [Object Order](../object_order.md) -- using an insertion-ordered `ObjectType`
- [`basic_json`](../../api/basic_json/index.md) -- API documentation of the class template
- [Types](index.md) - overview of how JSON values are stored
- [Number Handling](number_handling.md) - how the number types affect parsing and serialization
- [Object Order](../object_order.md) - using an insertion-ordered `ObjectType`
- [`basic_json`](../../api/basic_json/index.md) - API documentation of the class template
- [`object_t`](../../api/basic_json/object_t.md) - the `ObjectType` in use
- [`array_t`](../../api/basic_json/array_t.md) - the `ArrayType` in use
- [`string_t`](../../api/basic_json/string_t.md) - the `StringType` in use
- [`json_serializer`](../../api/basic_json/json_serializer.md) - the `JSONSerializer` in use
- [`json_base_class_t`](../../api/basic_json/json_base_class_t.md) - the `CustomBaseClass` in use
- [`with_t`](../../api/basic_json/with_t.md) - derive a specialization with one parameter replaced
- [`get_allocator`](../../api/basic_json/get_allocator.md) - the allocator in use