Files
json/docs/mkdocs/docs/features/object_order.md
T
Niels Lohmann 681fb07eb2 docs: cover fifo_map, gtl, folly::sorted_vector_map, and Qt
nlohmann::fifo_map works through the adapter that has always been documented
for it, and preserves the insertion order. Restore its mention in the object
order page, which was dropped together with the tsl::ordered_map one: unlike
ordered_map it keeps a lookup index, so it is the insertion-ordered option
without the quadratic cost.

gtl::flat_hash_map and folly::sorted_vector_map work as well, the latter
through an alias that drops the allocator, whose value type it disagrees on.
gtl::btree_map does not, for the same reason as the other btree containers.

None of the Qt containers can be used, each for its own reason: QMap has no
value_type, QHash iterators yield the mapped value rather than a pair, QList
has no max_size(), QByteArray spells empty() as isEmpty(), and QString is
UTF-16.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-28 17:38:57 +00:00

3.6 KiB

Object Order

The JSON standard defines objects as "an unordered collection of zero or more name/value pairs". As such, an implementation does not need to preserve any specific order of object keys.

Default behavior: sort keys

The default type nlohmann::json uses a std::map to store JSON objects, and thus stores object keys sorted alphabetically.

??? example

```cpp
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    json j;
    j["one"] = 1;
    j["two"] = 2;
    j["three"] = 3;
    
    std::cout << j.dump(2) << '\n';
}
```

Output:

```json
{
  "one": 1,
  "three": 3,
  "two": 2
}
```

Alternative behavior: preserve insertion order

If you do want to preserve the insertion order, you can use the type nlohmann::ordered_json.

??? example

```cpp
--8<-- "examples/ordered_json.cpp"
```

Output:

```json
--8<-- "examples/ordered_json.output"
```

Alternatively, nlohmann::fifo_map also preserves the insertion order and, unlike ordered_map, keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter (integration).

If the order does not matter and you only want faster lookup, boost::unordered_flat_map, absl::flat_hash_map, absl::node_hash_map, and several other hash maps work through an adapter that restores the template argument order basic_json expects; see Template Parameter Requirements. Note these are unordered, not insertion-ordered.

tsl::ordered_map cannot be used: its iterators expose the mapped value as const, while basic_json needs to modify it in place.

The ordered_map behind nlohmann::ordered_json is deliberately minimal and has no lookup index, so every key access is a linear scan and building an object of n keys costs O(n²). This is unnoticeable at typical object sizes but becomes significant for objects with many thousands of keys; see ordered_map complexity. The alternatives above keep a lookup index and do not have this cost.

Notes on parsing

Note that you also need to call the right parse function when reading from a file. Assume file input.json contains the JSON object above:

{
  "one": 1,
  "two": 2,
  "three": 3
}

!!! success "Right way"

The following code correctly calls the `parse` function from `nlohmann::ordered_json`:

```cpp
std::ifstream i("input.json");
auto j = nlohmann::ordered_json::parse(i);
std::cout << j.dump(2) << std::endl;
```

The output will be:

```json
{
  "one": 1,
  "two": 2,
  "three": 3
}
```

??? failure "Wrong way"

The following code incorrectly calls the `parse` function from `nlohmann::json` which does not preserve the
insertion order, but sorts object keys. Assigning the result to `nlohmann::ordered_json` compiles, but does not
restore the order from the input file.

```cpp
std::ifstream i("input.json");
nlohmann::ordered_json j = nlohmann::json::parse(i);
std::cout << j.dump(2) << std::endl;
```

The output will be:

```json
{
  "one": 1,
  "three": 3,
  "two": 2
}
```