* Review and extend the documentation, and check it in CI A review of all documentation pages found factual errors, dead links, missing cross-references, and gaps in examples. This fixes them and adds checks so the same problems are caught automatically. Fixes: - wrong signatures and version histories (operator!= C++20 member, binary() subtype type, get<PointerType>(), JSON_NO_THREAD_LOCAL, ...) - stale descriptions (number parsing since #5283, UBJSON table, SAX example that no longer compiled, tsl::ordered_map advice) - dead internal and external links; repology.org badges (the domain is suspended) replaced by badges that query the registries directly - deprecation notes link the migration guide; the guide itself fixed Additions: - "See also" sections, cross-references, 25 runnable examples, 12 Mermaid diagrams, new API pages for json_pointer::operator<=> and byte_container_with_subtype::operator==/!= - landing page, guides for untrusted input and performance - "unreleased" badge after versions newer than the latest release Checks: - strict documentation build (broken links/anchors fail it); CI and the publish workflow fetch the full history the build needs - weekly external link check, Mermaid syntax check in CI - check_structure.py: example titles, heading levels, alt texts, header links, docset index coverage; its unused-example check works again - all examples produce the same output on every platform Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the customer links that could not be fixed A dead link on the customers page is still the evidence of where the use of the library was documented. Keep the original URLs of the entries without a working replacement (Marne, Cisco Webex Desk Camera, Philips Hue, CyberArk) and exclude exactly these URLs from the link check. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Correct the duplicate-key recipe's claim about SAX positions The SAX interface's key() receives no position either; only parse_error() does. Also note that the recipe does not report the path to the repeated key (see discussion #5085). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Say the library is available as a single header and mention json_fwd.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Correct documentation errors found while hunting for bugs - patch/patch_inplace: list the JSON pointer errors parse_error.106-109 and out_of_range.402/404, and quote the actual parse_error.105 message. - unflatten: list parse_error.106/107/108 and out_of_range.404. - to_bson: list out_of_range.415 (binary subtype above 255) and note that 412 and 415 are new in 3.13.0. - to_string: state that string_t must be convertible to std::string, also in the StringType requirements table. - JSON Lines: a `while (input >> j)` loop also throws after the last value for concatenated JSON values; show a loop that works for both. - BON8: a string gets 0xFF only if nothing follows it in the message; a string at the end of an array or object is ended by 0xFE. - custom_string_type.hpp: add operator+=(char), which the "Always required" list asks for (json_pointer::to_string, flatten, unflatten, and diff did not compile), and an ADL int_to_string for diff and items. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Cache the release headers with functools.lru_cache Codacy (Pylint) flagged the mutable default argument that header() used as its cache. functools.lru_cache keeps the same memoization without it. The script's output is unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
120 lines
3.7 KiB
Markdown
120 lines
3.7 KiB
Markdown
# Object Order
|
|
|
|
The [JSON standard](https://tools.ietf.org/html/rfc8259.html) 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 "Example: `json` sorts object keys"
|
|
|
|
```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`](../api/ordered_json.md).
|
|
|
|
??? example "Example: `ordered_json` preserves insertion order"
|
|
|
|
```cpp
|
|
--8<-- "examples/ordered_json.cpp"
|
|
```
|
|
|
|
Output:
|
|
|
|
```json
|
|
--8<-- "examples/ordered_json.output"
|
|
```
|
|
|
|
Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
|
|
|
|
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](types/template_parameters.md#objecttype). Note these are *unordered*, not insertion-ordered.
|
|
|
|
[`tsl::ordered_map`](https://github.com/Tessil/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`](../api/ordered_map.md) 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](../api/ordered_map.md#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`](../api/basic_json/parse.md) function when reading from a file.
|
|
Assume file `input.json` contains the JSON object above:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
}
|
|
```
|