Files
json/docs/mkdocs/docs/features/element_access/unchecked_access.md
T
Niels Lohmann e9c3985f0a Fix documentation gaps found in a full GitHub Discussions review (#5264)
* 📡 Fix documentation gaps found in a full GitHub Discussions review

Reviewed all 1008 GitHub Discussions (2020-2026) for recurring questions
that better or more visible documentation would have avoided. Adds/expands
documentation for ~26 distinct gaps, including:

- New "Debugging" page collecting natvis, GDB pretty printer, LLDB status,
  and JSON_DIAGNOSTICS pointers (previously scattered/undiscoverable)
- Thread-safety and schema-validation FAQ entries
- StringType's char-based requirement (no wstring/u16string/u32string)
- Brace-initialization-yields-arrays warning directly on the constructor
  reference page (previously only in the FAQ, missed by users reading
  the constructor docs)
- std::any exclusion from get<T>(), with a manual-dispatch example
- Non-string-keyed std::map serializing as an array of pairs
- ordered_json compatibility with NLOHMANN_DEFINE_TYPE_* macros
  (already worked, was undocumented)
- std::array truncation on size-mismatched conversion (no exception)
- static_cast vs. get<std::optional<T>>() divergence
- Recipe for omitting a std::optional field instead of emitting null
- No built-in nesting-depth limit during parsing + a callback-based
  workaround recipe
- Recipe for streaming a large homogeneous array via parser callbacks
- operator>> stream-position semantics for concatenated JSON values
- JSON Pointer array-vs-object creation rule for non-existing paths
- CMake target name (nlohmann_json_modules) needed to link C++20 modules
- ESP-IDF/PlatformIO: no official package, link to a community fork
- get(key, default) as the Python dict.get() equivalent
- reserve() recipe for pre-allocating array capacity
- JSONC as an alias for the existing ignore_comments/ignore_trailing_commas
  combination (distinct from the unsupported JSON5)
- items() dereferenced-element type: decltype() idiom + detail-namespace
  stability caveat
- Various macro/type-conversion limitations (MSGPACK_DEFINE_ARRAY
  equivalent, char-array round-tripping, ADL serializer macro gap)

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

* 🚶 fix format

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 16:01:08 +02:00

6.0 KiB

Unchecked access: operator[]

Overview

Elements in a JSON object and a JSON array can be accessed via operator[] similar to a #!cpp std::map and a #!cpp std::vector, respectively.

??? example "Read access"

Consider the following JSON value:

```json
{
    "name": "Mary Smith",
    "age": 42,
    "hobbies": ["hiking", "reading"]
}
```

Assume the value is parsed to a `json` variable `j`.

| expression              | value                                                                        |
|-------------------------|------------------------------------------------------------------------------|
| `#!cpp j`               | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` |
| `#!cpp j["name"]`       | `#!json "Mary Smith"`                                                        |
| `#!cpp j["age"]`        | `#!json 42`                                                                  |
| `#!cpp j["hobbies"]`    | `#!json ["hiking", "reading"]`                                               |
| `#!cpp j["hobbies"][0]` | `#!json "hiking"`                                                            |
| `#!cpp j["hobbies"][1]` | `#!json "reading"`                                                           |

The return value is a reference, so it can modify the original value. In case the passed object key is non-existing, a #!json null value is inserted which can immediately be overwritten.

??? example "Write access"

```cpp
j["name"] = "John Smith";
j["maidenName"] = "Jones";
```

This code produces the following JSON value:

```json
{
    "name": "John Smith",
    "maidenName": "Jones",
    "age": 42,
    "hobbies": ["hiking", "reading"]
}
```

When accessing an invalid index (i.e., an index greater than or equal to the array size), the JSON array is resized such that the passed index is the new maximal index. Intermediate values are filled with #!json null.

??? example "Filling up arrays with #!json null values"

```cpp
j["hobbies"][0] = "running";
j["hobbies"][3] = "cooking";
```

This code produces the following JSON value:

```json
{
    "name": "John Smith",
    "maidenName": "Jones",
    "age": 42,
    "hobbies": ["running", "reading", null, "cooking"]
}
```

Notes

!!! info "Design rationale"

The library behaves differently to `#!cpp std::vector` and `#!cpp std::map`:

- `#!cpp std::vector::operator[]` never inserts a new element.
- `#!cpp std::map::operator[]` is not available for const values.

The type `#!cpp json` wraps all JSON value types. It would be impossible to remove
[`operator[]`](../../api/basic_json/operator%5B%5D.md) for const objects. At the same time, inserting elements for
non-const objects is really convenient as it avoids awkward `insert` calls. To this end, we decided to have an
inserting non-const behavior for both arrays and objects.

!!! info

The access is unchecked. In case the passed object key does not exist or the passed array index is invalid, no
exception is thrown.

!!! danger

- It is **undefined behavior** to access a const object with a non-existing key.
- It is **undefined behavior** to access a const array with an invalid index.
- In debug mode, an **assertion** will fire in both cases. You can disable assertions by defining the preprocessor
  symbol `#!cpp NDEBUG` or redefine the macro [`JSON_ASSERT(x)`](../macros.md#json_assertx). See the documentation
  on [runtime assertions](../assertions.md) for more information.

!!! failure "Exceptions"

`operator[]` can only be used with objects (with a string argument) or with arrays (with a numeric argument). For
other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error305) is thrown.

Performance: reserving array capacity

There is no public reserve(count) member on basic_json for pre-allocating array capacity. If you are building a large array incrementally (e.g., via repeated push_back()) and know its final size ahead of time, you can reserve capacity via get_ref() to access the underlying array_t directly:

json j = json::array();
j.get_ref<json::array_t&>().reserve(1000);
for (int i = 0; i < 1000; ++i) {
    j.push_back(i);
}

Summary

scenario non-const value const value
access to existing object key reference to existing value is returned const reference to existing value is returned
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 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 in debug mode