mirror of
https://github.com/nlohmann/json.git
synced 2026-10-30 09:25:42 +00:00
* 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>
278 lines
14 KiB
Markdown
278 lines
14 KiB
Markdown
# Converting values
|
|
|
|
A `basic_json` value stores JSON data, but most of the time you want to move that data into ordinary C++ types (an
|
|
`#!cpp int`, a `#!cpp std::string`, a `#!cpp std::vector`, or one of your own structs) and back. This page describes how
|
|
these conversions work.
|
|
|
|
## Getting values out
|
|
|
|
The [`get`](../api/basic_json/get.md) function template returns a copy of the stored value converted to the requested
|
|
type:
|
|
|
|
```cpp
|
|
json j = R"({"name": "Mary", "age": 42, "hobbies": ["hiking", "reading"]})"_json;
|
|
|
|
auto name = j["name"].get<std::string>(); // "Mary"
|
|
auto age = j["age"].get<int>(); // 42
|
|
auto hobbies = j["hobbies"].get<std::vector<std::string>>(); // {"hiking", "reading"}
|
|
```
|
|
|
|
!!! note "Getting a string without quotes"
|
|
|
|
A frequent point of confusion: use [`get`](../api/basic_json/get.md), **not** [`dump`](serialization.md), to read a
|
|
string value. `#!cpp j["name"].get<std::string>()` yields `#!cpp Mary`, whereas `#!cpp j["name"].dump()` yields the
|
|
JSON text `#!cpp "Mary"` (**with** quotes), because `dump` always produces a JSON text.
|
|
|
|
Alternatively, [`get_to`](../api/basic_json/get_to.md) writes into an existing variable and deduces the target type,
|
|
which avoids repeating it:
|
|
|
|
??? example
|
|
|
|
```cpp
|
|
--8<-- "examples/get_to.cpp"
|
|
```
|
|
|
|
Output:
|
|
|
|
```json
|
|
--8<-- "examples/get_to.output"
|
|
```
|
|
|
|
!!! info "Getting values out of a document or a view"
|
|
|
|
A [view](../api/basic_json_view/index.md) offers the same `get<T>()` conversion. For the built-in types (booleans,
|
|
numbers, strings, `#!cpp std::vector`, `#!cpp std::map`, ...) it reads the value straight from the document's
|
|
index, without building a `basic_json` value first; user-defined types with a `from_json()` are converted from a
|
|
`basic_json` value, see the [list](../api/basic_json_view/get.md). Two conversions never copy at all:
|
|
[`get_string()`](../api/basic_json_view/get_string.md) returns a string as a view into the source text, and
|
|
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
|
|
type is obtained from a subtree that is built on demand with
|
|
[`materialize()`](../api/basic_json_view/materialize.md). See
|
|
[Zero-copy JSON views](json_view.md#getting-values-out-without-copying).
|
|
|
|
The library already knows how to convert to and from the scalar types and the STL containers (such as
|
|
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
|
|
object back to a `#!cpp std::map` or a JSON array back to a `#!cpp std::vector` therefore works without any extra code:
|
|
|
|
```cpp
|
|
json j = {{"one", 1}, {"two", 2}};
|
|
auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}}
|
|
```
|
|
|
|
`#!cpp std::pair` and `#!cpp std::tuple` are also supported, converting positionally to and from a JSON array:
|
|
|
|
```cpp
|
|
json j = {1.0, "hello", 42};
|
|
auto t = j.get<std::tuple<double, std::string, int>>(); // {1.0, "hello", 42}
|
|
```
|
|
|
|
!!! warning "Serializing a `std::pair`/`std::tuple` whose every element is a string-keyed pair"
|
|
|
|
When *every* element of a `#!cpp std::pair` or `#!cpp std::tuple` is itself a two-element array whose first
|
|
element is a string (for example `#!cpp std::pair<std::string, int>`), serializing it produces a JSON **object**
|
|
instead of the expected array:
|
|
|
|
```cpp
|
|
using kv = std::pair<std::string, int>;
|
|
json j = std::pair<kv, kv>{{"a", 1}, {"b", 2}}; // {"a":1,"b":2}, not [["a",1],["b",2]]
|
|
```
|
|
|
|
This is a consequence of the [brace-initializer object-detection rule](creating_values.md): the same rule that
|
|
lets `#!cpp json{{"a", 1}, {"b", 2}}` create an object also fires here. The resulting object cannot be read back
|
|
into the original type (`#!cpp get<std::pair<kv, kv>>()` throws [`type_error.302`](../home/exceptions.md#jsonexceptiontype_error302)),
|
|
and duplicate keys collapse into one, losing elements. This only affects `#!cpp std::pair`/`#!cpp std::tuple`
|
|
themselves; a `#!cpp std::vector<std::pair<std::string, int>>`, or a pair/tuple with at least one element that is
|
|
not a string-keyed pair, serializes to an array as expected. To force an array, build one explicitly from the
|
|
elements with [`array`](../api/basic_json/array.md):
|
|
|
|
```cpp
|
|
std::pair<kv, kv> p{{"a", 1}, {"b", 2}};
|
|
json a = json::array({p.first, p.second}); // [["a",1],["b",2]]
|
|
```
|
|
|
|
!!! info "Extracting references into a tuple"
|
|
|
|
A tuple type may also hold references (e.g. `#!cpp std::tuple<double&, std::string&>`) to avoid copying: `get`
|
|
then returns a tuple of references pointing directly at the elements stored inside the `basic_json` array,
|
|
rather than a tuple of copies:
|
|
|
|
```cpp
|
|
json j = {1.0, "hello"};
|
|
auto refs = j.get<std::tuple<double&, std::string&>>();
|
|
std::get<1>(refs) = "world"; // modifies j[1] in place
|
|
```
|
|
|
|
A referenced element must name the type the library actually *stores* — one of [`boolean_t`](../api/basic_json/boolean_t.md),
|
|
[`number_integer_t`](../api/basic_json/number_integer_t.md), [`number_unsigned_t`](../api/basic_json/number_unsigned_t.md),
|
|
[`number_float_t`](../api/basic_json/number_float_t.md), [`string_t`](../api/basic_json/string_t.md),
|
|
[`binary_t`](../api/basic_json/binary_t.md), [`array_t`](../api/basic_json/array_t.md), or
|
|
[`object_t`](../api/basic_json/object_t.md). There is nothing else to refer to, so a reference to any other type is a
|
|
compile error even when a conversion would exist: `#!cpp std::tuple<int&>` is rejected, because the library stores a
|
|
`#!cpp number_integer_t` (`#!cpp std::int64_t` by default) and not an `#!cpp int`. This restriction applies only to
|
|
reference elements — a plain `#!cpp std::tuple<int>` converts by value as usual.
|
|
|
|
## Implicit conversions
|
|
|
|
By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted:
|
|
|
|
```cpp
|
|
json j = "Hello";
|
|
std::string s = j; // implicit conversion, same as j.get<std::string>()
|
|
```
|
|
|
|
Implicit conversions are convenient but can be surprising (for example, in overload resolution or with `auto`). They can
|
|
be disabled by defining [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) to `#!cpp 0`,
|
|
which forces the explicit `get` form and can catch unintended conversions at compile time.
|
|
|
|
!!! warning "`auto` copies the JSON value; it does not convert it"
|
|
|
|
The type of `#!cpp j["name"]` is `json`, not the C++ type of the value inside. `#!cpp auto` therefore deduces
|
|
`json`:
|
|
|
|
```cpp
|
|
json j = {{"name", "Mary"}, {"age", 42}};
|
|
|
|
auto name = j["name"]; // json: a copy of the value; prints as "Mary" (with quotes)
|
|
std::string s1 = j["name"]; // std::string: implicit conversion
|
|
auto s2 = j["name"].get<std::string>(); // std::string: explicit conversion
|
|
auto& age = j["age"]; // json&: a reference, no copy
|
|
age = 43; // changes j
|
|
```
|
|
|
|
This is also why `#!cpp std::cout << j["name"]` prints the quotes (see
|
|
[getting a string without quotes](#getting-values-out)), and why a `json` value cannot be passed to `printf`: the
|
|
argument is not an `int` or a `char*`, and a compiler either rejects passing it through `...` (Clang does) or
|
|
compiles it and prints garbage. Convert first: `#!cpp std::printf("%d", j["age"].get<int>());`.
|
|
|
|
Prefer `get<T>()` over `auto` when you want the C++ value, and `auto&` or `const auto&` when you want to avoid a
|
|
copy of a large value.
|
|
|
|
!!! warning "Conversions do not range-check numbers"
|
|
|
|
Just like C++ itself, the `get` family performs numeric conversions without range checks — retrieving a
|
|
floating-point value as an integer truncates it, and narrowing conversions may overflow. See
|
|
[number conversion](types/number_handling.md#number-conversion) for details and how to guard against it.
|
|
|
|
!!! warning "std::optional direct construction from JSON null throws"
|
|
|
|
Constructing or assigning `std::optional<T>` directly from a JSON value does not correctly produce
|
|
`std::nullopt` for a JSON `null`:
|
|
|
|
```cpp
|
|
json j_null;
|
|
std::optional<std::string> opt = j_null; // ❌ throws type_error 302
|
|
```
|
|
|
|
This is due to C++ language rules: `std::optional<T>` has its own converting constructor that is chosen over
|
|
`basic_json::operator T()` when both are viable. Use `get<std::optional<T>>()` or `get_to()` instead:
|
|
|
|
```cpp
|
|
auto opt = j_null.get<std::optional<std::string>>(); // ✅ std::nullopt
|
|
j_null.get_to(opt); // ✅ std::nullopt
|
|
```
|
|
|
|
!!! warning "`static_cast` and `get<std::optional<T>>()` are not guaranteed equivalent"
|
|
|
|
`operator ValueType()` (used by `static_cast` and implicit conversions) intentionally excludes
|
|
`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,
|
|
with a custom `adl_serializer<std::optional<T>>` specialization. Prefer `get<std::optional<T>>()`/`get_to()`
|
|
over `static_cast` for optional types.
|
|
|
|
!!! warning "Converting to a fixed-size destination does not check the array size"
|
|
|
|
Some destination types have a size that is fixed by their C++ type rather than by the JSON value:
|
|
`#!cpp std::pair<A, B>`, `#!cpp std::tuple<Ts...>`, `#!cpp std::array<T, N>`, C arrays `#!cpp T[N]`, and
|
|
`#!cpp std::map`/`#!cpp std::unordered_map` with a non-string key type (which is read from an array of
|
|
two-element arrays). All of them read exactly as many elements as they need via
|
|
[`at`](../api/basic_json/at.md) and **never compare the JSON array's size to that number**. The two
|
|
mismatch directions therefore behave differently:
|
|
|
|
- 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 —
|
|
an out-of-range error, not a [`type_error`](../home/exceptions.md#type-errors), even though the cause
|
|
is a shape mismatch.
|
|
|
|
```cpp
|
|
json j = {1, 2, 3, 4, 5};
|
|
|
|
auto a = j.get<std::array<int, 3>>(); // {1, 2, 3} -- elements 4 and 5 silently dropped
|
|
auto p = j.get<std::pair<int, int>>(); // (1, 2) -- elements 3, 4, and 5 silently dropped
|
|
|
|
json k = {1};
|
|
auto q = k.get<std::pair<int, int>>(); // ❌ throws out_of_range.401
|
|
```
|
|
|
|
If a size mismatch is an error in your application, check the size yourself before converting.
|
|
|
|
## 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
|
|
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
|
|
implemented in the *containing* type's `to_json`:
|
|
|
|
```cpp
|
|
struct person {
|
|
std::string name;
|
|
std::optional<int> age;
|
|
};
|
|
|
|
void to_json(json& j, const person& p) {
|
|
j = json{{"name", p.name}};
|
|
if (p.age) {
|
|
j["age"] = *p.age; // key is only inserted when the optional has a value
|
|
}
|
|
}
|
|
```
|
|
|
|
## Putting values in
|
|
|
|
The reverse direction works the same way: assigning or constructing a `json` from a C++ value converts it to JSON.
|
|
|
|
```cpp
|
|
std::vector<int> numbers = {1, 2, 3};
|
|
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:
|
|
|
|
```cpp
|
|
std::vector<int> nums{1, 2, 37, 42, 21};
|
|
auto filtered = nums | std::views::filter([](int i) { return i > 10; });
|
|
json j(filtered); // [37,42,21]
|
|
```
|
|
|
|
This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
|
|
to incomplete C++20 ranges support there; see
|
|
[`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md).
|
|
|
|
## Your own types
|
|
|
|
The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
|
|
`to_json`/`from_json` functions (or use one of the convenience macros). This is described in detail on the
|
|
[arbitrary types conversions](arbitrary_types.md) page. Enums can be mapped to strings as described in
|
|
[specializing enum conversion](enum_conversion.md).
|
|
|
|
## See also
|
|
|
|
- [`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
|
|
- [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
|
|
|