# 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(); // "Mary" auto age = j["age"].get(); // 42 auto hobbies = j["hobbies"].get>(); // {"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()` 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" ``` 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>(); // {{"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>(); // {1.0, "hello", 42} ``` !!! info "Extracting references into a tuple" A tuple type may also hold references (e.g. `#!cpp std::tuple`) 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::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` 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` 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() ``` 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 "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` directly from a JSON value does not correctly produce `std::nullopt` for a JSON `null`: ```cpp json j_null; std::optional opt = j_null; // ❌ throws type_error 302 ``` This is due to C++ language rules: `std::optional` has its own converting constructor that is chosen over `basic_json::operator T()` when both are viable. Use `get>()` or `get_to()` instead: ```cpp auto opt = j_null.get>(); // ✅ std::nullopt j_null.get_to(opt); // ✅ std::nullopt ``` !!! warning "`static_cast` and `get>()` are not guaranteed equivalent" `operator ValueType()` (used by `static_cast` and implicit conversions) intentionally excludes `std::optional` from delegating to `get()`, to avoid a constructor ambiguity with `std::optional`'s own converting constructor from `basic_json`. As a result, `static_cast>(json_value)` goes through `std::optional`'s own converting constructor rather than through `get>()`, which can behave differently -- for example, with a custom `adl_serializer>` specialization. Prefer `get>()`/`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`, `#!cpp std::tuple`, `#!cpp std::array`, 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>(); // {1, 2, 3} -- elements 4 and 5 silently dropped auto p = j.get>(); // (1, 2) -- elements 3, 4, and 5 silently dropped json k = {1}; auto q = k.get>(); // ❌ 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` 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>` 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 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 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 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. ## 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