# 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"
    ```

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 "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.

## 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
