Compare commits

...
Author SHA1 Message Date
Niels Lohmann f53413325b Merge remote-tracking branch 'origin/develop' into claude/nlohmann-json-docs-b89a59
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 06:31:57 +01:00
Niels Lohmann 7b2a1b2de5 Docs: fill documentation gaps found in user questions
Add pages on class-hierarchy/smart-pointer/variant/chrono recipes, working
without exceptions, and large, streamed and partial input. Extend the FAQ
(23 entries, Known bugs first), parse error documentation (empty-input
checklist, line/column, diagnostic positions), element access, conversions,
creating values, ADL troubleshooting, memory, and build/distribution notes.
Add 14 examples with generated output. Correct the statement that parse errors
cannot be recovered from with -fno-exceptions.

The topics come from recurring questions on Stack Overflow, Reddit, Hacker
News, YouTube, and the project's GitHub discussions and issues.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-24 19:13:49 +02:00
45 changed files with 1666 additions and 8 deletions

No files matched your search

@@ -0,0 +1,42 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
json j = json::parse(R"({"name": "Mary", "age": 42})");
// look up the key, then test the type: neither step throws
for (const char* key : {"name", "age", "city"})
{
const auto it = j.find(key);
if (it == j.end())
{
std::cout << key << ": missing\n";
}
else if (it->is_string())
{
std::cout << key << ": string \"" << it->get_ref<const std::string&>() << "\"\n";
}
else
{
std::cout << key << ": not a string, but " << it->type_name() << '\n';
}
}
// get_ptr returns a null pointer if the value has another type
const std::string* name = j["name"].get_ptr<const std::string*>();
const std::string* age = j["age"].get_ptr<const std::string*>();
std::cout << "get_ptr<string*>: name = " << (name != nullptr) << ", age = " << (age != nullptr) << '\n';
// value() is NOT exception-free: it throws if the key exists with a different type
try
{
std::cout << j.value("name", 0) << '\n';
}
catch (const json::type_error& e)
{
std::cout << e.what() << '\n';
}
}
@@ -0,0 +1,5 @@
name: string "Mary"
age: not a string, but number
city: missing
get_ptr<string*>: name = 1, age = 0
[json.exception.type_error.302] type must be number, but is string
@@ -0,0 +1,46 @@
#include <iostream>
#include <optional>
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
struct person
{
std::string name;
int age;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age)
// Compile this function with exceptions enabled and call it from code that does not use them.
std::optional<person> to_person(const json& j)
{
try
{
return j.get<person>();
}
catch (const json::exception&)
{
return std::nullopt;
}
}
int main()
{
const json good = {{"name", "Mary"}, {"age", 42}};
const json bad = {{"name", "Mary"}, {"age", "forty-two"}};
const json incomplete = {{"name", "Mary"}};
for (const json& j : {good, bad, incomplete})
{
if (const auto p = to_person(j))
{
std::cout << "person: " << p->name << ", " << p->age << '\n';
}
else
{
std::cout << "not a person: " << j << '\n';
}
}
}
@@ -0,0 +1,3 @@
person: Mary, 42
not a person: {"age":"forty-two","name":"Mary"}
not a person: {"name":"Mary"}
@@ -0,0 +1,28 @@
#include <cstdint>
#include <iostream>
#include <vector>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// None of the calls below throws, so this example also works with -fno-exceptions.
int main()
{
const std::string invalid = R"({"name": "Mary", "age": )";
// parse() with allow_exceptions = false returns a discarded value
json j = json::parse(invalid, nullptr, false);
std::cout << std::boolalpha << "parse: discarded = " << j.is_discarded() << '\n';
// accept() only tells whether the input is valid JSON
std::cout << "accept: " << json::accept(invalid) << '\n';
// the binary formats have the same parameter
const std::vector<std::uint8_t> truncated_cbor = {0x83, 0x01};
json c = json::from_cbor(truncated_cbor, true, false);
std::cout << "from_cbor: discarded = " << c.is_discarded() << '\n';
// valid input is parsed as usual
json valid = json::parse(R"({"name": "Mary"})", nullptr, false);
std::cout << "valid input: " << valid << '\n';
}
@@ -0,0 +1,4 @@
parse: discarded = true
accept: false
from_cbor: discarded = true
valid input: {"name":"Mary"}
@@ -0,0 +1,42 @@
#include <iostream>
#include <utility>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// line and column (both starting at 1) of a byte index in a text
std::pair<std::size_t, std::size_t> line_column(const std::string& input, std::size_t byte)
{
std::size_t line = 1;
std::size_t column = 1;
for (std::size_t i = 0; i + 1 < byte && i < input.size(); ++i)
{
if (input[i] == '\n')
{
++line;
column = 1;
}
else
{
++column;
}
}
return {line, column};
}
int main()
{
const std::string input = "{\n \"a\": 1,\n \"b\": ]\n}";
try
{
const json j = json::parse(input);
std::cout << j << '\n';
}
catch (const json::parse_error& e)
{
const auto position = line_column(input, e.byte);
std::cout << e.what() << '\n';
std::cout << "byte " << e.byte << " = line " << position.first << ", column " << position.second << '\n';
}
}
@@ -0,0 +1,2 @@
[json.exception.parse_error.101] parse error at line 3, column 8: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
byte 20 = line 3, column 8
@@ -0,0 +1,46 @@
#define JSON_DIAGNOSTIC_POSITIONS 1
#include <iostream>
#include <utility>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// line and column (both starting at 1) of a byte index in a text
std::pair<std::size_t, std::size_t> line_column(const std::string& input, std::size_t byte)
{
std::size_t line = 1;
std::size_t column = 1;
for (std::size_t i = 0; i + 1 < byte && i < input.size(); ++i)
{
if (input[i] == '\n')
{
++line;
column = 1;
}
else
{
++column;
}
}
return {line, column};
}
int main()
{
const std::string input = "{\n \"name\": \"Mary\",\n \"age\": \"forty-two\"\n}";
const json j = json::parse(input); // the input is valid JSON ...
try
{
const int age = j["age"]; // ... but the value has the wrong type
std::cout << age << '\n';
}
catch (const json::exception& e)
{
std::cout << e.what() << '\n';
// start_pos() is a 0-based index into the input; the helper expects a 1-based byte index
const auto position = line_column(input, j["age"].start_pos() + 1);
std::cout << "the value starts at line " << position.first << ", column " << position.second << '\n';
}
}
@@ -0,0 +1,2 @@
[json.exception.type_error.302] (bytes 29-40) type must be number, but is string
the value starts at line 3, column 10
@@ -0,0 +1,46 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
std::string buffer;
// call for every chunk; returns true and sets `value` once the buffer holds one complete JSON value
bool push(const std::string& chunk, json& value)
{
buffer += chunk;
try
{
value = json::parse(buffer);
buffer.clear();
return true;
}
catch (const json::parse_error& e)
{
if (e.byte == buffer.size() + 1)
{
return false; // the input ends prematurely: wait for more data
}
throw; // a real syntax error
}
}
int main()
{
json value;
for (const std::string chunk : {"{\"na", "me\": \"Ma", "ry\", \"l", "ist\": [1, 2", ", 3]}"})
{
std::cout << "chunk " << chunk << " -> " << (push(chunk, value) ? "complete" : "incomplete") << '\n';
}
std::cout << value << '\n';
// a syntax error in the middle is not mistaken for missing data
try
{
push("[1, ]", value);
}
catch (const json::parse_error& e)
{
std::cout << e.what() << '\n';
}
}
@@ -0,0 +1,7 @@
chunk {"na -> incomplete
chunk me": "Ma -> incomplete
chunk ry", "l -> incomplete
chunk ist": [1, 2 -> incomplete
chunk , 3]} -> complete
{"list":[1,2,3],"name":"Mary"}
[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
@@ -0,0 +1,23 @@
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// a CBOR value followed by other data
const std::vector<std::uint8_t> cbor = json::to_cbor(json{1, 2, 3});
std::string data(cbor.begin(), cbor.end());
data += "TRAILING";
std::istringstream input(data);
json j = json::from_cbor(input, false); // strict = false: stop after the first value
std::cout << "value: " << j << '\n';
std::cout << "bytes used: " << input.tellg() << '\n';
std::string rest;
input >> rest;
std::cout << "remaining input: " << rest << '\n';
}
@@ -0,0 +1,3 @@
value: [1,2,3]
bytes used: 4
remaining input: TRAILING
@@ -0,0 +1,18 @@
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::istringstream input(R"({"a":1} [2,3] 4 "s")");
// check for the end of the stream before each read, because a read at the end throws
json j;
while (input >> std::ws && input.peek() != std::char_traits<char>::eof())
{
input >> j;
std::cout << j << '\n';
}
}
@@ -0,0 +1,4 @@
{"a":1}
[2,3]
4
"s"
@@ -0,0 +1,27 @@
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
void handle(const json& record)
{
std::cout << "handling " << record << '\n';
}
int main()
{
std::istringstream file(R"([{"id": 1}, {"id": 2}, {"id": 3}])");
json result = json::parse(file, [](int depth, json::parse_event_t event, json & record)
{
if (depth == 1 && event == json::parse_event_t::object_end)
{
handle(record);
return false; // discard the record
}
return true;
});
std::cout << "result: " << result << '\n';
}
@@ -0,0 +1,4 @@
handling {"id":1}
handling {"id":2}
handling {"id":3}
result: []
@@ -0,0 +1,81 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
struct find_id : nlohmann::json_sax<json>
{
bool next_is_id = false;
json::number_unsigned_t id = 0;
bool key(std::string& name) override
{
next_is_id = (name == "id");
return true;
}
bool number_unsigned(json::number_unsigned_t value) override
{
if (next_is_id)
{
id = value;
return false; // found: stop parsing
}
return true;
}
bool null() override
{
return true;
}
bool boolean(bool /*val*/) override
{
return true;
}
bool number_integer(json::number_integer_t /*val*/) override
{
return true;
}
bool number_float(json::number_float_t /*val*/, const std::string& /*s*/) override
{
return true;
}
bool string(std::string& /*val*/) override
{
return true;
}
bool binary(json::binary_t& /*val*/) override
{
return true;
}
bool start_object(std::size_t /*elements*/) override
{
return true;
}
bool end_object() override
{
return true;
}
bool start_array(std::size_t /*elements*/) override
{
return true;
}
bool end_array() override
{
return true;
}
bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& /*ex*/) override
{
return false;
}
};
int main()
{
find_id handler;
const bool completed = json::sax_parse(R"({"x": [1, 2, 3], "id": 42, "rest": [4, 5, 6]})", &handler);
std::cout << std::boolalpha;
std::cout << "parse completed: " << completed << '\n';
std::cout << "id: " << handler.id << '\n';
}
@@ -0,0 +1,2 @@
parse completed: false
id: 42
@@ -0,0 +1,56 @@
#include <chrono>
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace nlohmann
{
template <typename Rep, typename Period>
struct adl_serializer<std::chrono::duration<Rep, Period>>
{
static void to_json(json& j, const std::chrono::duration<Rep, Period>& d)
{
j = d.count();
}
static void from_json(const json& j, std::chrono::duration<Rep, Period>& d)
{
d = std::chrono::duration<Rep, Period>(j.get<Rep>());
}
};
template <typename Duration>
struct adl_serializer<std::chrono::time_point<std::chrono::system_clock, Duration>>
{
static void to_json(json& j, const std::chrono::time_point<std::chrono::system_clock, Duration>& t)
{
j = t.time_since_epoch();
}
static void from_json(const json& j, std::chrono::time_point<std::chrono::system_clock, Duration>& t)
{
t = std::chrono::time_point<std::chrono::system_clock, Duration>(j.get<Duration>());
}
};
} // namespace nlohmann
int main()
{
using namespace std::chrono;
std::cout << std::boolalpha;
// the unit is not part of the JSON value
const json timeout = milliseconds(1500);
std::cout << "duration: " << timeout << '\n';
std::cout << "as milliseconds: " << timeout.get<milliseconds>().count() << '\n';
// a system_clock time point is the time since the Unix epoch
const time_point<system_clock, milliseconds> t(milliseconds(1700000000123));
const json stamp = t;
std::cout << "time point: " << stamp << '\n';
const auto back = stamp.get<time_point<system_clock, milliseconds>>();
std::cout << "round trip: " << (back == t) << '\n';
}
@@ -0,0 +1,4 @@
duration: 1500
as milliseconds: 1500
time point: 1700000000123
round trip: true
@@ -0,0 +1,99 @@
#include <iostream>
#include <memory>
#include <stdexcept>
#include <vector>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
struct shape
{
virtual ~shape() = default;
virtual json serialize() const = 0;
};
struct circle : shape
{
double radius;
explicit circle(double r) : radius(r) {}
json serialize() const override
{
return {{"type", "circle"}, {"radius", radius}};
}
};
struct rectangle : shape
{
double width;
double height;
rectangle(double w, double h) : width(w), height(h) {}
json serialize() const override
{
return {{"type", "rectangle"}, {"width", width}, {"height", height}};
}
};
namespace nlohmann
{
template <>
struct adl_serializer<std::unique_ptr<shape>>
{
static void to_json(json& j, const std::unique_ptr<shape>& p)
{
j = p ? p->serialize() : json(nullptr);
}
static void from_json(const json& j, std::unique_ptr<shape>& p)
{
if (j.is_null())
{
p.reset();
return;
}
const auto& type = j.at("type").get_ref<const std::string&>();
if (type == "circle")
{
p = std::make_unique<circle>(j.at("radius").get<double>());
}
else if (type == "rectangle")
{
p = std::make_unique<rectangle>(j.at("width").get<double>(), j.at("height").get<double>());
}
else
{
throw std::invalid_argument("unknown shape type: " + type);
}
}
};
} // namespace nlohmann
int main()
{
std::vector<std::unique_ptr<shape>> shapes;
shapes.push_back(std::make_unique<circle>(1.5));
shapes.push_back(std::make_unique<rectangle>(2, 3));
const json j = shapes;
std::cout << j << '\n';
// reading creates objects of the derived types
const auto back = j.get<std::vector<std::unique_ptr<shape>>>();
std::cout << std::boolalpha;
std::cout << "first is a circle: " << (dynamic_cast<circle*>(back[0].get()) != nullptr) << '\n';
std::cout << "second is a rectangle: " << (dynamic_cast<rectangle*>(back[1].get()) != nullptr) << '\n';
// an unknown type tag is reported
try
{
json{{"type", "star"}}.get<std::unique_ptr<shape>>();
}
catch (const std::invalid_argument& e)
{
std::cout << e.what() << '\n';
}
}
@@ -0,0 +1,4 @@
[{"radius":1.5,"type":"circle"},{"height":3.0,"type":"rectangle","width":2.0}]
first is a circle: true
second is a rectangle: true
unknown shape type: star
@@ -0,0 +1,90 @@
#include <iostream>
#include <memory>
#include <vector>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace nlohmann
{
template <typename T>
struct adl_serializer<std::shared_ptr<T>>
{
static void to_json(json& j, const std::shared_ptr<T>& p)
{
if (p)
{
j = *p;
}
else
{
j = nullptr;
}
}
static void from_json(const json& j, std::shared_ptr<T>& p)
{
if (j.is_null())
{
p = nullptr;
}
else
{
p = std::make_shared<T>(j.get<typename std::remove_const<T>::type>());
}
}
};
template <typename T>
struct adl_serializer<std::unique_ptr<T>>
{
static void to_json(json& j, const std::unique_ptr<T>& p)
{
if (p)
{
j = *p;
}
else
{
j = nullptr;
}
}
static void from_json(const json& j, std::unique_ptr<T>& p)
{
if (j.is_null())
{
p = nullptr;
}
else
{
p = std::make_unique<T>(j.get<T>());
}
}
};
} // namespace nlohmann
struct point
{
int x;
int y;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(point, x, y)
int main()
{
// an empty pointer is written as null
const std::vector<std::shared_ptr<point>> points = {std::make_shared<point>(point{1, 2}), nullptr};
const json j = points;
std::cout << j << '\n';
// reading allocates new objects
const auto back = j.get<std::vector<std::shared_ptr<point>>>();
std::cout << back[0]->x << ", " << back[0]->y << ", " << (back[1] == nullptr) << '\n';
// pointers to const and unique_ptr work as well
const auto const_ptr = j[0].get<std::shared_ptr<const point>>();
const auto unique = j[0].get<std::unique_ptr<point>>();
std::cout << const_ptr->y << ", " << unique->x << '\n';
}
@@ -0,0 +1,3 @@
[{"x":1,"y":2},null]
1, 2, 1
2, 1
@@ -0,0 +1,63 @@
#include <iostream>
#include <stdexcept>
#include <variant>
#include <vector>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace geo
{
struct circ
{
double radius;
};
struct rect
{
double width;
double height;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(circ, radius)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(rect, width, height)
using figure = std::variant<circ, rect>;
void to_json(json& j, const figure& f)
{
std::visit([&j](const auto & alternative)
{
j = alternative;
}, f);
j["type"] = std::holds_alternative<circ>(f) ? "circ" : "rect";
}
void from_json(const json& j, figure& f)
{
const auto& type = j.at("type").get_ref<const std::string&>();
if (type == "circ")
{
f = j.get<circ>();
}
else if (type == "rect")
{
f = j.get<rect>();
}
else
{
throw std::invalid_argument("unknown figure type: " + type);
}
}
} // namespace geo
int main()
{
const std::vector<geo::figure> figures = {geo::circ{2}, geo::rect{3, 4}};
const json j = figures;
std::cout << j << '\n';
const auto back = j.get<std::vector<geo::figure>>();
std::cout << "alternatives read back: " << back[0].index() << ", " << back[1].index() << '\n';
}
@@ -0,0 +1,2 @@
[{"radius":2.0,"type":"circ"},{"height":4.0,"type":"rect","width":3.0}]
alternatives read back: 0, 1
@@ -409,6 +409,32 @@ namespace nlohmann {
} }
``` ```
## Why isn't my `to_json` or `from_json` found?
The library calls `to_json` and `from_json` without qualification, so the compiler finds them by
[argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) in the namespace of the type, or in the
namespaces of its template arguments. The usual reasons for a function that is "not found" are:
- **The type is an alias of a standard type.** After `#!cpp using Hash = std::array<std::uint8_t, 6>;` the type
*is* `std::array<std::uint8_t, 6>`, which lives in namespace `std`. A `to_json(json&, const Hash&)` in your namespace
is never selected. Either give the type its own identity, for example `#!cpp struct Hash : std::array<std::uint8_t, 6> {};`
(the function is then found in `Hash`'s namespace), or specialize
[`adl_serializer<Hash>`](#how-do-i-convert-third-party-types).
- **The function is in the wrong namespace.** It must be in the namespace of the type (a global type needs a global
function), not in the namespace of the code that calls the conversion. Specializations of `adl_serializer` belong in
namespace `nlohmann`.
- **It is declared too late.** A conversion can only use what has been declared when it is instantiated. If
`#!cpp json j = std::vector<third_party::string>{...};` appears before the `adl_serializer` specialization, the
compiler reports an error such as "explicit specialization of `adl_serializer<...>` after instantiation". Put the
specialization in a small header that every file includes **before** it uses the type with `json`.
- **It is a template.** For `template <class T> struct box`, write `template <class T> void to_json(json&, const box<T>&)`,
not an overload for one `T`.
- **It worked by accident.** Some versions found `to_json` overloads in `nlohmann::detail` through argument-dependent
lookup of the base class of `basic_json`. Do not rely on this; it is not part of the interface.
For smart pointers, class hierarchies, `std::variant`, and `std::chrono`, see
[Recipes for common types](type_recipes.md).
## Why can't I convert to/from `std::any`? ## Why can't I convert to/from `std::any`?
`std::any` is intentionally excluded from `get<T>()`/generic conversion support, so `get<std::any>()` and `std::any` is intentionally excluded from `get<T>()`/generic conversion support, so `get<std::any>()` and
@@ -505,6 +531,7 @@ struct bad_serializer
## See also ## See also
- [Recipes for common types](type_recipes.md) - smart pointers, class hierarchies, `std::variant`, and `std::chrono`
- [Converting values](conversions.md) - the general overview of `get`/`get_to` and implicit conversions - [Converting values](conversions.md) - the general overview of `get`/`get_to` and implicit conversions
- [Specializing enum conversion](enum_conversion.md) - map enums to JSON strings instead of integers - [Specializing enum conversion](enum_conversion.md) - map enums to JSON strings instead of integers
- [Supported macros](macros.md) - reference for `NLOHMANN_DEFINE_TYPE_*` and related macros - [Supported macros](macros.md) - reference for `NLOHMANN_DEFINE_TYPE_*` and related macros
+3 -1
View File
@@ -7,7 +7,9 @@ detect bugs in client code.
## Switch off runtime assertions ## Switch off runtime assertions
Runtime assertions can be switched off by defining the preprocessor macro `NDEBUG` (see the Runtime assertions can be switched off by defining the preprocessor macro `NDEBUG` (see the
[documentation of assert](https://en.cppreference.com/w/cpp/error/assert)) which is the default for release builds. [documentation of assert](https://en.cppreference.com/w/cpp/error/assert)) which is the default for release builds. Without `NDEBUG`, the assertions are compiled in and contain the name of the source
file (`__FILE__`); see [notes for builds and distribution](../integration/index.md#notes-for-builds-and-distribution)
if that path must not end up in your binaries.
## Change assertion behavior ## Change assertion behavior
+23
View File
@@ -124,6 +124,29 @@ Implicit conversions are convenient but can be surprising (for example, in overl
be disabled by defining [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) to `#!cpp 0`, 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. which forces the explicit `get` form and can catch unintended conversions at compile time.
!!! warning "`auto` copies the JSON value; it does not convert it"
The type of `#!cpp j["name"]` is `json`, not the C++ type of the value inside. `#!cpp auto` therefore deduces
`json`:
```cpp
json j = {{"name", "Mary"}, {"age", 42}};
auto name = j["name"]; // json: a copy of the value; prints as "Mary" (with quotes)
std::string s1 = j["name"]; // std::string: implicit conversion
auto s2 = j["name"].get<std::string>(); // std::string: explicit conversion
auto& age = j["age"]; // json&: a reference, no copy
age = 43; // changes j
```
This is also why `#!cpp std::cout << j["name"]` prints the quotes (see
[getting a string without quotes](#getting-values-out)), and why a `json` value cannot be passed to `printf`: the
argument is not an `int` or a `char*`, and a compiler either rejects passing it through `...` (Clang does) or
compiles it and prints garbage. Convert first: `#!cpp std::printf("%d", j["age"].get<int>());`.
Prefer `get<T>()` over `auto` when you want the C++ value, and `auto&` or `const auto&` when you want to avoid a
copy of a large value.
!!! warning "Conversions do not range-check numbers" !!! warning "Conversions do not range-check numbers"
Just like C++ itself, the `get` family performs numeric conversions without range checks — retrieving a Just like C++ itself, the `get` family performs numeric conversions without range checks — retrieving a
@@ -61,6 +61,25 @@ with a string as the first element is treated as an object, everything else as a
[FAQ](../home/faq.md#brace-initialization-yields-arrays) for details and the opt-in [FAQ](../home/faq.md#brace-initialization-yields-arrays) for details and the opt-in
[`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) macro. [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) macro.
### What does this produce?
The following table shows how some common spellings are interpreted. When in doubt, use `#!cpp json::array()`,
`#!cpp json::object()`, or the `#!cpp json(value)` form with parentheses.
| code | result | note |
|-------------------------------------------------|------------------------------|----------------------------------------------|
| `#!cpp json j = {};` / `#!cpp json j{};` | `#!json null` | use `json::object()` or `json::array()` |
| `#!cpp json j = json::object();` | `#!json {}` | |
| `#!cpp json j = json::array();` | `#!json []` | |
| `#!cpp json j(true);` | `#!json true` | parentheses create the value itself |
| `#!cpp json j = {true};` | `#!json [true]` | braces create a one-element array (`#!cpp json j{true};` depends on the compiler, see the [FAQ](../home/faq.md#brace-initialization-yields-arrays)) |
| `#!cpp json j = {{"a", 1}};` | `#!json {"a":1}` | all elements are `[string, value]` pairs: object |
| `#!cpp json j = {{"a", 1}, {"b"}};` | `#!json [["a",1],["b"]]` | one element is not a pair: array |
| `#!cpp json j = {{"a", 1}, 2};` | `#!json [["a",1],2]` | a plain value among pairs: array |
| `#!cpp json j = {{"a", 1, 2}};` | `#!json [["a",1,2]]` | a three-element list is not a pair: array |
| `#!cpp json j = "{\"a\":1}";` | `#!json "{\"a\":1}"` | a string **value**; it is not parsed |
| `#!cpp json j = json::parse("{\"a\":1}");` | `#!json {"a":1}` | parse text with `parse` or `_json` |
## Building incrementally ## Building incrementally
A value can also be built up piece by piece. Accessing a non-existing object key or array index with A value can also be built up piece by piece. Accessing a non-existing object key or array index with
@@ -22,6 +22,45 @@ flowchart TD
F -->|"yes"| G["contains() / find()"] F -->|"yes"| G["contains() / find()"]
``` ```
## Does this key exist?
Use [`contains`](../../api/basic_json/contains.md) to test for a key; it does not modify the value and does not throw:
```cpp
json j = {{"name", "Mary"}, {"address", {{"city", "Berlin"}}}};
j.contains("name"); // true
j.contains("age"); // false
j.contains("city"); // false: contains() only looks at the top level
j["address"].contains("city"); // true
j.contains(json::json_pointer("/address/city")); // true: a JSON pointer tests a nested path in one call
```
If the value is needed afterwards, [`find`](../../api/basic_json/find.md) tests and accesses with a single lookup:
```cpp
if (auto it = j.find("name"); it != j.end())
{
std::cout << *it << '\n';
}
```
`contains` also returns `true` for a key whose value is `null`; use `j.contains("k") && !j["k"].is_null()` if a
`null` should count as missing. To read a value with a fallback instead, see [`value`](default_value.md).
!!! warning "`operator[]` inserts missing keys"
Like `#!cpp std::map::operator[]`, the non-const [`operator[]`](unchecked_access.md) adds a `null` value if the key
is missing. Testing a key with it therefore *changes the object*:
```cpp
json j = {{"foo", 1}};
bool absent = j["bar"].is_null(); // true, but j is now {"bar": null, "foo": 1}
```
Use `contains`/`find` to test, and [`at`](checked_access.md) to access a value that must exist (it throws an
[`out_of_range`](../../home/exceptions.md#out-of-range) exception otherwise).
## See also ## See also
- [**operator[]**](../../api/basic_json/operator%5B%5D.md) - unchecked access - [**operator[]**](../../api/basic_json/operator%5B%5D.md) - unchecked access
+113
View File
@@ -0,0 +1,113 @@
# Working Without Exceptions
By default, the library reports errors by throwing exceptions (see [Exceptions](../home/exceptions.md)). This page
collects what to use when exceptions are unwanted or unavailable, for example in embedded systems, game engines, or
code compiled with `-fno-exceptions`.
## Two different questions
1. **Can my code avoid *catching* exceptions?** Yes. For parsing and for accessing values there are functions that
report failure through a return value. They work in a normal build and need no `#!cpp try`/`#!cpp catch`.
2. **Can the library be *compiled* without exceptions?** Yes, with `-fno-exceptions` or by defining
[`JSON_NOEXCEPTION`](../api/macros/json_noexception.md). Both have the same effect: every `#!cpp throw` in the
library becomes a call to `std::abort()`. The functions listed below never reach a `#!cpp throw` for the failures
they report, so they keep working in this configuration. Anything else that fails aborts the program.
## Parsing
| function | on invalid input |
|---------------------------------------------------------------------------------------------|-----------------------------------------------------|
| `#!cpp json::parse(input, nullptr, false)` | returns a *discarded* value; test with `is_discarded()` |
| [`accept`](../api/basic_json/accept.md) | returns `#!cpp false` |
| `#!cpp json::from_cbor(input, true, false)` (and the other `from_*` functions) | returns a *discarded* value |
| [`sax_parse`](../api/basic_json/sax_parse.md) with your own handler | returns `#!cpp false` after calling `parse_error` |
??? example "Example: parse and validate without exceptions"
```cpp
--8<-- "examples/no_exceptions_parse.cpp"
```
Output:
```
--8<-- "examples/no_exceptions_parse.output"
```
None of the calls in this example throws, so it also compiles and runs with `-fno-exceptions`.
The discarded value carries no message. If you need the position or the reason, use the SAX interface; see
[Parsing and exceptions](parsing/parse_exceptions.md).
## Accessing values
The access functions are not equally safe. Only some of them never throw:
| expression | if the key is missing or the type is wrong |
|-------------------------------------|-------------------------------------------------------------------------------|
| `#!cpp j.contains("k")` | returns `#!cpp false` for a missing key; never throws |
| `#!cpp j.find("k")` | returns `end()` for a missing key; never throws |
| `#!cpp j.is_string()`, ... | the `is_*` functions test the type; never throw |
| `#!cpp j.get_ptr<const std::string*>()` | returns `#!cpp nullptr` for a different type; never throws |
| `#!cpp j.at("k")` | throws `out_of_range.403` for a missing key |
| `#!cpp j.get<int>()` | throws `type_error.302` for a value that is not a number |
| `#!cpp j.value("k", 0)` | returns the default for a missing key, but **throws `type_error.302` if `k` exists with another type** |
| `#!cpp j["k"]` (non-const) | inserts `null` for a missing key |
| `#!cpp j["k"]` (const) | undefined behavior for a missing key |
So reading a value that may be absent or of the wrong type without exceptions takes two steps: look up, then test the
type.
??? example "Example: look up a key and test its type"
```cpp
--8<-- "examples/no_exceptions_access.cpp"
```
Output:
```
--8<-- "examples/no_exceptions_access.output"
```
The last lines of the example show why `value` is not on the safe list: it returns the default only for a *missing*
key.
For numbers, test with `is_number()` (or `is_number_integer()`, `is_number_unsigned()`, `is_number_float()`) before
calling `get<int>()`, and see [number handling](types/number_handling.md) for range and precision. Whether a value
fits into the target type is not checked by `get`.
## Converting to your own types
`from_json` functions (including the ones generated by the
[`NLOHMANN_DEFINE_TYPE_*` macros](arbitrary_types.md#simplify-your-life-with-macros)) report problems by throwing.
There is no variant that returns an error code. Without exceptions you have two options:
- Validate the JSON value yourself, as shown above, and fill your struct field by field.
- Convert in a function that is compiled **with** exceptions enabled and catches them at the boundary, returning
`std::optional` or an error code to the rest of your code:
??? example "Example: convert to a struct and report failure as std::optional"
```cpp
--8<-- "examples/no_exceptions_convert.c++17.cpp"
```
Output:
```
--8<-- "examples/no_exceptions_convert.c++17.output"
```
## Overriding what happens instead of an exception
With exceptions off, you can replace the `abort()` by your own code, for example to log the message first. See
[Switch off exceptions](../home/exceptions.md#switch-off-exceptions) and
[`JSON_THROW_USER`](../api/macros/json_throw_user.md). The handler must not return normally.
## See also
- [Exceptions](../home/exceptions.md) - all exceptions and their ids
- [Parsing and exceptions](parsing/parse_exceptions.md) - the parsing alternatives in detail
- [Parsing untrusted input](parsing/untrusted_input.md)
- [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md)
+13 -2
View File
@@ -36,7 +36,9 @@ json data = json::parse(f);
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
is read as UTF-16 or UTF-32 -- depending on the character type -- and converted to UTF-8; see the is read as UTF-16 or UTF-32 -- depending on the character type -- and converted to UTF-8; see the
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value. [`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format. Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format; input
that arrives in chunks or contains several values is covered in
[Large, streamed, and partial input](large_and_partial_input.md).
By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse` By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse`
function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md). function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md).
@@ -92,7 +94,7 @@ again without changing any result.
## SAX vs. DOM parsing ## SAX vs. DOM parsing
The library offers two parsing models: The library offers two parsing models, and a third, read-only alternative:
- **DOM parsing** (the default): the complete input is read and stored as an in-memory `basic_json` value that can be - **DOM parsing** (the default): the complete input is read and stored as an in-memory `basic_json` value that can be
traversed and modified freely. This is what [`parse`](../../api/basic_json/parse.md) does, and it is the right choice traversed and modified freely. This is what [`parse`](../../api/basic_json/parse.md) does, and it is the right choice
@@ -102,6 +104,13 @@ The library offers two parsing models:
inputs or when you only need to extract parts of the input. See the [SAX interface](sax_interface.md) for details and inputs or when you only need to extract parts of the input. See the [SAX interface](sax_interface.md) for details and
[`sax_parse`](../../api/basic_json/sax_parse.md) for the API. [`sax_parse`](../../api/basic_json/sax_parse.md) for the API.
- **Document and views** (`#!cpp <nlohmann/json_view.hpp>`, added in version 3.13.0): [`json_document::parse`](../../api/basic_json_document/parse.md)
reads the same JSON grammar but builds a flat, read-only index of the values instead of a `basic_json` tree. Strings and
floating-point numbers are read from the text on demand, and only the parts you
[`materialize()`](../../api/basic_json_view/materialize.md) become `basic_json` values. This avoids most of the work when you
need only a few values from a large or frequently parsed text; see [Zero-copy JSON views](../json_view.md). It is read-only, so use
`parse` when you need to modify the value.
You can influence a DOM parse without switching to the SAX interface by passing a You can influence a DOM parse without switching to the SAX interface by passing a
[parser callback](parser_callbacks.md), which is called during parsing and can, for example, discard parts of the input. [parser callback](parser_callbacks.md), which is called during parsing and can, for example, discard parts of the input.
@@ -139,8 +148,10 @@ options.
- [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON - [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events - [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
- [JSON Lines](json_lines.md) - parse newline-delimited JSON - [JSON Lines](json_lines.md) - parse newline-delimited JSON
- [Large, streamed, and partial input](large_and_partial_input.md) - chunked, concatenated, and very large input
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function - [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
- [SAX interface](sax_interface.md) - implement a custom SAX handler - [SAX interface](sax_interface.md) - implement a custom SAX handler
- [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views
- [parsing and exceptions](parse_exceptions.md) - control error handling - [parsing and exceptions](parse_exceptions.md) - control error handling
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources - [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
- [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view - [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view
@@ -0,0 +1,154 @@
# Large, Streamed, and Partial Input
[`parse`](../../api/basic_json/parse.md) reads exactly one complete JSON value and builds it in memory. It cannot be
paused and resumed, and it does not hand back a half-parsed value when the input ends early. This page shows what to do
when the input does not fit that model: it arrives in pieces, it contains several values, or it is too large to hold
as a `json` value.
```mermaid
flowchart TD
A["my input is..."] --> B{"several JSON values in one stream?"}
B -->|"yes"| C["read them one at a time"]
A --> D{"arriving in chunks (socket, pipe)?"}
D -->|"yes"| E["frame the messages, or buffer until complete"]
A --> F{"too large to keep in memory?"}
F -->|"yes"| G["discard parts with a callback, or use SAX"]
A --> H{"only a small part is needed?"}
H -->|"yes"| I["stop early with SAX, or read through a view"]
```
## Several values in one stream
For newline-delimited input, read line by line and parse each line, as described in [JSON Lines](json_lines.md). For
values that are simply concatenated, [`operator>>`](../../api/operator_gtgt.md) reads one value and leaves the stream
positioned after it. Check for the end of the stream before each read, because a read at the end throws:
??? example "Example: read concatenated values"
```cpp
--8<-- "examples/partial_input_concatenated.cpp"
```
Output:
```
--8<-- "examples/partial_input_concatenated.output"
```
The same works for binary formats. [`from_cbor`](../../api/basic_json/from_cbor.md) and its relatives accept
`#!cpp strict = false` to stop after the first value and leave the rest of the stream untouched; `tellg()` then reports
how many bytes the value took:
??? example "Example: read one CBOR value and find out how many bytes it used"
```cpp
--8<-- "examples/partial_input_cbor.cpp"
```
Output:
```
--8<-- "examples/partial_input_cbor.output"
```
## Input arriving in chunks
A socket, pipe, or HTTP body hands over data in pieces of arbitrary size, and a piece can end in the middle of a
number, a string, or an object. The parser does not keep state between calls, so there are two ways to deal with this.
**Frame the messages.** The most robust approach is a protocol that says where a value ends: one value per line
([JSON Lines](json_lines.md)), a length prefix, or a delimiter. Collect the bytes of one message and parse them when
the frame is complete. Prefer this whenever you control the protocol.
**Buffer until the value is complete.** If the data has no framing, append every chunk to a buffer and try to parse
it. A parse error at the very end of the buffer means that the input ended prematurely, while an error anywhere else is
a real syntax error. The member `byte` of the [`parse_error`](../../home/exceptions.md#jsonexceptionparse_error101)
exception is one past the buffer size in the first case:
??? example "Example: buffer chunks until the value is complete"
```cpp
--8<-- "examples/partial_input_buffering.cpp"
```
Output:
```
--8<-- "examples/partial_input_buffering.output"
```
The example feeds a document in five chunks. The first four are reported as incomplete, and a syntax error is reported as
such, not as missing data.
!!! warning "Limits of buffering"
- The buffer must contain exactly one value. Data that belongs to the next value is a syntax error. Use framing if
values follow each other without a separator.
- A number at the end of the buffer is a complete value, even if more digits are still to come (`12` may be the
start of `123`). Buffering cannot detect this; use framing for top-level numbers.
- Every attempt parses the buffer from the beginning, so the total work grows quadratically with the size of the
value. Limit the size of the buffer for input you do not trust (see [untrusted input](untrusted_input.md)).
## Input too large for memory
The input does not have to become one huge `json` value. The two tools below process it piece by piece.
### Handle and discard elements with a callback
A [parser callback](parser_callbacks.md) is called for every finished value. If it returns `#!cpp false`, the value is
discarded and its memory is released at once. For a file that consists of one huge array (or one huge object) of
records, handle each record at depth 1 and discard it:
??? example "Example: handle and discard each record of a large array"
```cpp
--8<-- "examples/partial_input_discard_records.cpp"
```
Output:
```
--8<-- "examples/partial_input_discard_records.output"
```
Only one record is in memory at any time. The same recipe, with more detail, is in the
[parser callbacks](parser_callbacks.md#recipe-streaming-a-large-homogeneous-array) page. To skip entire members you do not
need, discard them when their key is read; see the [example](parser_callbacks.md) for `parse_event_t::key`.
### Extract a few values and stop with SAX
With the [SAX interface](sax_interface.md), no value is built at all, and **returning `#!cpp false` from any handler
function stops the parser**. [`sax_parse`](../../api/basic_json/sax_parse.md) then returns `#!cpp false`, and the rest
of the input is not read. The example finds the first `id` in a document without parsing the remainder:
??? example "Example: stop parsing as soon as a value is found"
```cpp
--8<-- "examples/partial_input_sax_stop.cpp"
```
Output:
```
--8<-- "examples/partial_input_sax_stop.output"
```
Note that `sax_parse` returns `#!cpp false` both when a handler stopped the parse and when the input is invalid, so
record the reason in the handler (here: `id`) if you need to tell the two apart.
### Read parts of a large document with a view
If the whole text is available and you only need to read some of it, a [`json_document`](../json_view.md) from
`<nlohmann/json_view.hpp>` (added in version 3.13.0) avoids the `basic_json` tree altogether: it keeps the text, builds a flat index of the values,
and gives read-only [views](../../api/basic_json_view/index.md) of the parts you look at. Only what you
[`materialize()`](../../api/basic_json_view/materialize.md) becomes a `basic_json` value. See
[Zero-copy JSON views](../json_view.md) for details and for the lifetime rules.
## See also
- [Parsing](index.md) - overview of the parsing functions
- [JSON Lines](json_lines.md) - newline-delimited JSON
- [Parser callbacks](parser_callbacks.md) - discard values while parsing
- [SAX interface](sax_interface.md) - event-based parsing
- [Zero-copy JSON views](../json_view.md) - read parts of a large document without building a tree
- [Untrusted input](untrusted_input.md) - limits for input you do not control
@@ -21,6 +21,10 @@ catch (json::parse_error& ex)
In case exceptions are undesired or not supported by the environment, there are different ways to proceed: In case exceptions are undesired or not supported by the environment, there are different ways to proceed:
!!! tip
[Working without exceptions](../no_exceptions.md) also covers accessing values and converting to your own types.
## Switch off exceptions ## Switch off exceptions
The [`parse()`](../../api/basic_json/parse.md) function accepts a `#!cpp bool` parameter `allow_exceptions` which The [`parse()`](../../api/basic_json/parse.md) function accepts a `#!cpp bool` parameter `allow_exceptions` which
@@ -81,3 +85,61 @@ The return value indicates whether the parsing should continue, so the function
``` ```
--8<-- "examples/sax_no_exception.output" --8<-- "examples/sax_no_exception.output"
``` ```
## Where did the error occur?
The message of a [`parse_error`](../../home/exceptions.md#jsonexceptionparse_error101) for JSON text contains the line
and column of the error:
```
[json.exception.parse_error.101] parse error at line 3, column 8: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
```
The exception object itself only stores the **byte index** of the last read character in the member `byte` (the first
byte of the input has index 1). The same index is passed to `parse_error` in the [SAX interface](sax_interface.md). If
you need the line and column as numbers, for example to show them in an editor, compute them from the input:
??? example "Example: compute line and column of a parse error"
```cpp
--8<-- "examples/parse_error_position.cpp"
```
Output:
```
--8<-- "examples/parse_error_position.output"
```
Columns count bytes, so a line with multi-byte UTF-8 characters before the error may differ from the column your editor
shows.
### Where is the value that caused a later error?
A `parse_error` is raised while the text is read. Many mistakes show up later: the text is valid JSON, but a value has
the wrong type when you convert it (`type_error.302`), or a key is missing (`out_of_range.403`). The exception then says
what is wrong, but not where in the input the value was. Define
[`JSON_DIAGNOSTIC_POSITIONS`](../../api/macros/json_diagnostic_positions.md) before including the library to add the
byte range of the offending value to such messages (since version 3.12.0), and to query the range of any value with
[`start_pos()`](../../api/basic_json/start_pos.md) and [`end_pos()`](../../api/basic_json/end_pos.md):
??? example "Example: locate a value with diagnostic positions"
```cpp
--8<-- "examples/parse_error_position_diagnostic.cpp"
```
Output:
```
--8<-- "examples/parse_error_position_diagnostic.output"
```
Positions are only available for values created by [`parse`](../../api/basic_json/parse.md), they are not updated when a
value is changed, and the macro makes every value 16 bytes larger on 64-bit platforms, so use it where the cost is
acceptable, for example for configuration files. Note that `start_pos()` is a 0-based index into the input, whereas
`byte` of a `parse_error` starts at 1.
For binary formats such as CBOR, there are no lines; `byte` is the position in the byte sequence. To find out how many
bytes a successfully parsed value used, see
[Several values in one stream](large_and_partial_input.md#several-values-in-one-stream).
@@ -26,9 +26,11 @@ several alternatives; see [Parsing and exceptions](parse_exceptions.md) for the
[example](parse_exceptions.md#user-defined-sax-interface) that overrides it to print instead of throw. [example](parse_exceptions.md#user-defined-sax-interface) that overrides it to print instead of throw.
If exceptions are unavailable entirely (`-fno-exceptions`, or [`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md) If exceptions are unavailable entirely (`-fno-exceptions`, or [`JSON_NOEXCEPTION`](../../api/macros/json_noexception.md)
defined), every `#!cpp throw` in the library becomes a call to `std::abort()` -- there is no way to recover from a defined), every `#!cpp throw` in the library becomes a call to `std::abort()`. The alternatives above do not throw, so
parse error of untrusted input in that configuration; see `parse()` with `allow_exceptions` set to `#!cpp false` (and likewise `accept()` and the `from_*` functions with
[Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for the details and for overriding this with `allow_exceptions` set to `#!cpp false`) can still reject invalid input in that configuration; a plain `parse()` call
aborts on invalid input. See [Working without exceptions](../no_exceptions.md) and
[Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for the details and for overriding the abort with
`JSON_THROW_USER`. `JSON_THROW_USER`.
## Nesting depth ## Nesting depth
+26 -1
View File
@@ -122,7 +122,9 @@ an adapter, since their template argument order does not match what `basic_json`
[linear in its size](../api/basic_json/basic_json.md#complexity) for the copy constructor but [linear in its size](../api/basic_json/basic_json.md#complexity) for the copy constructor but
[constant](../api/basic_json/basic_json.md#complexity) for the move constructor. The same applies to assigning a [constant](../api/basic_json/basic_json.md#complexity) for the move constructor. The same applies to assigning a
large `#!cpp std::string`, `#!cpp std::vector`, or other container into a value: pass it as `#!cpp std::move(x)` large `#!cpp std::string`, `#!cpp std::vector`, or other container into a value: pass it as `#!cpp std::move(x)`
rather than `x` whenever `x` is no longer needed afterwards. rather than `x` whenever `x` is no longer needed afterwards. Since version 3.13.0, converting an rvalue container
(`#!cpp json j = std::move(vec);`) moves the elements instead of copying them; the source is then left in a valid but
unspecified state.
- **Access without copying.** [`get<T>()`](../api/basic_json/get.md) returns a copy of the stored value converted to - **Access without copying.** [`get<T>()`](../api/basic_json/get.md) returns a copy of the stored value converted to
`T`. When a reference or pointer to the value already stored inside the `basic_json` is enough, `T`. When a reference or pointer to the value already stored inside the `basic_json` is enough,
[`get_ref()`](../api/basic_json/get_ref.md) and [`get_ptr()`](../api/basic_json/get_ptr.md) access it directly: [`get_ref()`](../api/basic_json/get_ref.md) and [`get_ptr()`](../api/basic_json/get_ptr.md) access it directly:
@@ -151,6 +153,29 @@ an adapter, since their template argument order does not match what `basic_json`
["reserving array capacity"](element_access/unchecked_access.md#performance-reserving-array-capacity) for the ["reserving array capacity"](element_access/unchecked_access.md#performance-reserving-array-capacity) for the
one-line recipe. one-line recipe.
## Memory
- **A value is small, its content is on the heap.** A `basic_json` is 16 bytes on a 64-bit platform (a type tag and a
union). Strings, arrays, objects, and binary values are allocated on the heap and owned by the value. Destroying the
value frees everything it owns.
- **Copies are deep.** After `#!cpp json b = a;`, changing `b` does not change `a`, and both own their memory. Copying a
large value therefore doubles its memory; see [Avoiding copies](#avoiding-copies). `#!cpp auto x = j["key"];` is a
copy, `#!cpp auto& x = j["key"];` is not.
- **A moved-from value is `null`.** After `#!cpp json c = std::move(a);` the value `a` is `#!json null` and owns nothing.
- **Reading does not require a tree.** If you only read the data, parse it into a [`json_document`](json_view.md)
(`#!cpp <nlohmann/json_view.hpp>`, added in version 3.13.0) instead of a `basic_json`. It stores 16 bytes per value, does not copy floating-point
numbers or strings without escape sequences out of the text, and creates no `basic_json` value except for the parts
you [`materialize()`](../api/basic_json_view/materialize.md). A document that borrows the text needs the text to
outlive it; see [Zero-copy JSON views](json_view.md).
- **Containers keep their capacity.** `clear()` and `erase()` on an array remove the elements, but the underlying
`#!cpp std::vector` keeps its allocated capacity, like `#!cpp std::vector::clear` does. To give the memory back,
assign a new value (`#!cpp j = json::array();`) or destroy the value.
- **Freed memory does not always go back to the operating system.** The library returns memory to the allocator
(`#!cpp std::allocator` by default, or the [one you configured](types/template_parameters.md)). Whether the process
size shrinks then depends on the allocator and the platform, as for any C++ program. A process that parses a large
document and drops it is not leaking if its resident size stays high; run it under a tool such as
[Valgrind](https://valgrind.org) or the sanitizers to tell a leak from retained memory.
## Serialization ## Serialization
[`dump()`](../api/basic_json/dump.md) with the default `#!cpp indent = -1` selects "the most compact representation" [`dump()`](../api/basic_json/dump.md) with the default `#!cpp indent = -1` selects "the most compact representation"
+122
View File
@@ -0,0 +1,122 @@
# Recipes for Common Types
The library converts the standard containers, strings, numbers, tuples, `std::optional`, and `std::filesystem::path`
(the last two since version 3.12.0, C++17) out of the box, and your own types once you write conversion functions or use
the macros (see [Arbitrary type conversions](arbitrary_types.md)). It deliberately does **not** convert smart pointers,
`std::variant`, `std::chrono` types, or class hierarchies, because there is more than one reasonable JSON
representation for each of them. This page shows a representation for each that you can copy and adapt.
All recipes use a specialization of [`adl_serializer`](../api/adl_serializer/index.md), which you may provide for types
you do not own, or free `to_json`/`from_json` functions in the namespace of the type. Place them where they are
visible before any code that converts the type; see
[Why isn't my `to_json` found?](arbitrary_types.md#why-isnt-my-to_json-or-from_json-found).
## Smart pointers
A `std::shared_ptr<T>` or `std::unique_ptr<T>` is written as the value it points to, or as `#!json null` for an empty
pointer, and read back by allocating a new `T`:
??? example "Example: serialize shared_ptr and unique_ptr"
```cpp
--8<-- "examples/type_recipes_smart_pointers.c++14.cpp"
```
Output:
```
--8<-- "examples/type_recipes_smart_pointers.c++14.output"
```
With this, containers of smart pointers, pointers to `const`, and members of these types in your own structs convert as
usual. A pointer to a base class cannot be filled this way, because `T` would have
to be created as a derived type; see the next section.
Reading creates a new object. Two `shared_ptr`s that pointed to the same object are written twice and read back as two
separate objects.
## Class hierarchies
!!! tip "Derived classes whose type is known at compile time"
If you only need to serialize the data members of a derived class, and the code always knows the concrete type, you do
not need a type tag. The [`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros](arbitrary_types.md#simplify-your-life-with-macros)
(since version 3.12.0) add the members of the derived class to those of its base class. This section is for the case
where the concrete type is only known at run time, such as a `std::unique_ptr<shape>` that holds a circle or a
rectangle.
JSON has no notion of types, so a value in a hierarchy needs a **type tag** that says which derived class to create.
Write the tag in the object, and select the class from it when reading:
??? example "Example: serialize a class hierarchy with a type tag"
```cpp
--8<-- "examples/type_recipes_class_hierarchy.c++14.cpp"
```
Output:
```
--8<-- "examples/type_recipes_class_hierarchy.c++14.output"
```
A `#!cpp std::vector<std::unique_ptr<shape>>` converts to an array of tagged objects and back, creating a `circle`
and a `rectangle`. The same works for `std::shared_ptr<shape>`. Remember the virtual destructor: without it, deleting a
derived object through the base pointer is undefined behavior.
## `std::variant`
A variant has the same problem: the alternative must be recorded. If the alternatives are your own types, write a type
tag next to their members, exactly as for a hierarchy:
??? example "Example: serialize a std::variant with a type tag"
```cpp
--8<-- "examples/type_recipes_variant.c++17.cpp"
```
Output:
```
--8<-- "examples/type_recipes_variant.c++17.output"
```
In the example, the functions are found
because `geo` is the namespace of the template arguments of `#!cpp std::variant<circ, rect>`; for a variant of only
standard types (for example `#!cpp std::variant<int, std::string>`) there is no such namespace, and you need an
`adl_serializer` specialization or a wrapper type of your own.
If the alternatives are JSON-native (numbers, strings, ...), a variant such as `#!cpp std::variant<int, std::string>`
can instead be written as the plain value, and read by testing the JSON type (`is_number_integer()`, `is_string()`)
to choose the alternative.
## `std::chrono`
A duration is written as its count and read back with the same representation and period. The **unit is not part of
the JSON value**, so document it, for example in the member name (`timeout_ms`):
??? example "Example: serialize durations and system_clock time points"
```cpp
--8<-- "examples/type_recipes_chrono.cpp"
```
Output:
```
--8<-- "examples/type_recipes_chrono.output"
```
A `system_clock::time_point` with millisecond precision is written as the number of milliseconds since the Unix epoch.
The time point serializer is limited to `system_clock`,
because the epoch of other clocks (for example `steady_clock`) has no meaning outside the running program.
If you prefer a text representation such as ISO 8601 (`2023-11-14T22:13:20Z`), write the string with
`#!cpp std::format("{:%FT%TZ}", t)` and read it with `std::chrono::parse` (both C++20), or with a date library, in the
same two functions.
## See also
- [Arbitrary type conversions](arbitrary_types.md) - the conversion mechanism and macros
- [Why isn't my `to_json` found?](arbitrary_types.md#why-isnt-my-to_json-or-from_json-found)
- [Converting values](conversions.md) - `get`, implicit conversions, and `std::optional`
+30
View File
@@ -39,6 +39,8 @@ classDiagram
Exceptions are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md). In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `#!cpp throw`), `JSON_TRY_USER` (overriding `#!cpp try`), and `JSON_CATCH_USER` (overriding `#!cpp catch`). Exceptions are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md). In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `#!cpp throw`), `JSON_TRY_USER` (overriding `#!cpp try`), and `JSON_CATCH_USER` (overriding `#!cpp catch`).
See [Working without exceptions](../features/no_exceptions.md) for the functions that report errors without throwing.
Note that [`JSON_THROW_USER`](../api/macros/json_throw_user.md) should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. Note that [`JSON_THROW_USER`](../api/macros/json_throw_user.md) should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior.
??? example "Example: switch off exceptions and log errors before aborting" ??? example "Example: switch off exceptions and log errors before aborting"
@@ -193,6 +195,34 @@ This error indicates a syntax error while deserializing a JSON text. The error m
- Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened. - Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened.
- Paste the input to a JSON validator like <https://jsonlint.com> or a tool like [jq](https://jqlang.github.io/jq/). - Paste the input to a JSON validator like <https://jsonlint.com> or a tool like [jq](https://jqlang.github.io/jq/).
??? info "Parsing a file fails at line 1, column 1 (or \"unexpected end of input\")"
The messages `attempting to parse an empty input` (since version 3.11.3) and `unexpected end of input` (also the message of
earlier versions for an empty input) at the very start of the input mean
that the parser did not see a single character. The library only reads from the stream it is given: it does not
open files and cannot tell a missing file from an empty one. The usual causes are:
- **The file was not opened.** A relative path is resolved against the *working directory of the running program*,
not against the source file or the project. IDEs and build tools often start the program in a build directory
(for example `build/` or `cmake-build-debug/`), so `"example.json"` may need to be `"../example.json"`. Use an
absolute path to rule this out.
- **The file is empty**, or a previous run truncated it (for example when it was opened for writing).
- **The stream was already read.** A stream whose content has been consumed (by printing it with
`std::cout << file.rdbuf()`, or by an earlier `accept`/`parse`/`>>`) is at its end. Parse it once, or rewind it
with `seekg(0)` after clearing the state with `clear()`.
Check the stream before parsing it:
```cpp
std::ifstream f("example.json");
if (!f.is_open())
{
std::cerr << "cannot open example.json\n";
return 1;
}
json data = json::parse(f);
```
### json.exception.parse_error.102 ### json.exception.parse_error.102
JSON uses the `\uxxxx` format to describe Unicode characters. Code points above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. JSON uses the `\uxxxx` format to describe Unicode characters. Code points above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
+234 -1
View File
@@ -58,6 +58,208 @@ json j{obj}; // -> {"key":"value"} (copy, not array)
Without the macro (default behavior), `json j{obj}` creates `[{"key":"value"}]`. This opt-in macro fixes issue #5074 while preserving backwards compatibility for existing code. Without the macro (default behavior), `json j{obj}` creates `[{"key":"value"}]`. This opt-in macro fixes issue #5074 while preserving backwards compatibility for existing code.
## Getting started
### Which file do I need?
!!! question
Do I have to clone the repository or build the library?
No. The library is header-only: the single file [`json.hpp`](https://github.com/nlohmann/json/releases) from the
releases page is all you need, including the `_json` literals. Put it in a directory `nlohmann` on your include path.
Companion headers (`json_fwd.hpp`, `json_view.hpp`, `json_literals.hpp`) are only needed if you use them. See
[Integration](../integration/index.md#header-only).
### Using CMake or a package manager
!!! question
How do I add the library with CMake, Conan, vcpkg, Homebrew, or another tool?
See [CMake](../integration/cmake.md) for `find_package`, `add_subdirectory`, and `FetchContent`, and
[Package managers](../integration/package_managers.md) for the list of supported package managers.
### The IDE cannot open the header
!!! question
The compiler finds `nlohmann/json.hpp`, but my IDE reports "cannot open source file". Why?
The compiler's include path is set correctly, but the IDE's is not. Add the same directory to the include paths of the
IDE (or of its language server); the build itself is not affected.
### Compile times
!!! question
Including the library slows down my build. What can I do?
Include [`json_fwd.hpp`](../integration/compile_times.md#include-json_fwdhpp-in-headers) in headers and `json.hpp` only
in the source files that need it, instantiate `basic_json` once, and consider precompiled headers or
[C++20 modules](../features/modules.md). See [Compile times](../integration/compile_times.md).
## Parsing
### Parsing a file fails with "unexpected end of input"
!!! question
`json::parse(file)` throws `parse_error.101` at line 1, column 1 for a file that is valid JSON. Why?
The parser did not see any character: the file was most likely not opened. A relative path is resolved against the
working directory of the running program, which is often the build directory of your IDE, not the source directory.
Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101).
### Input that arrives in chunks
!!! question
My data comes from a socket in pieces. How do I parse it?
The parser cannot be resumed. Frame the messages (one value per line, a length prefix) and parse each complete frame, or
buffer the chunks until the value is complete; a `parse_error` at the end of the buffer means the input ended
prematurely. See [Large, streamed, and partial input](../features/parsing/large_and_partial_input.md#input-arriving-in-chunks).
### Several values in one stream
!!! question
How do I read concatenated JSON values or one value per line?
Read newline-delimited input line by line ([JSON Lines](../features/parsing/json_lines.md)). For concatenated values, use
`operator>>` and check for the end of the stream before each read, as shown in
[Several values in one stream](../features/parsing/large_and_partial_input.md#several-values-in-one-stream).
### Files that are too large for memory
!!! question
How do I process a huge JSON file?
Handle and discard the records of a large array with a [parser callback](../features/parsing/parser_callbacks.md), stop
early with the [SAX interface](../features/parsing/sax_interface.md), or read parts of the text through a
[`json_document`](../features/json_view.md) (added in version 3.13.0). See
[Input too large for memory](../features/parsing/large_and_partial_input.md#input-too-large-for-memory).
### Position of a parse error
!!! question
How do I get the line and column of a parse error?
The message contains them. The exception stores the byte index in `byte`, from which you can compute both; see
[Where did the error occur?](../features/parsing/parse_exceptions.md#where-did-the-error-occur). To locate a value that
causes a *later* error, such as a `type_error`, use
[`JSON_DIAGNOSTIC_POSITIONS`](../features/parsing/parse_exceptions.md#where-is-the-value-that-caused-a-later-error).
### Comments and trailing commas
!!! question
Can the parser read JSON with comments or trailing commas?
Yes, when asked to: see [Comments](../features/comments.md) and [Trailing commas](../features/trailing_commas.md). Both
are rejected by default.
## Accessing and creating values
### Reading a value into a variable
!!! question
How do I get a C++ value out of a `json` value?
Use [`get<T>()`](../api/basic_json/get.md), for example `#!cpp int age = j["age"].get<int>();`, or an implicit conversion.
`#!cpp auto x = j["age"];` is **not** a conversion: it copies the `json` value. See
[Converting values](../features/conversions.md).
### Strings are printed with quotes
!!! question
`std::cout << j["name"]` prints `"Mary"` with quotes. How do I get the string?
The value is a `json`, and printing it produces JSON text. Convert it first: `#!cpp j["name"].get<std::string>()`. See
[Getting a string without quotes](../features/conversions.md#getting-values-out).
### Testing whether a key exists
!!! question
How do I check if a key exists, without changing the object?
Use `contains` or `find`. Do not test with the non-const `operator[]`: like `std::map::operator[]`, it inserts a `null`
value for a missing key. See [Does this key exist?](../features/element_access/index.md#does-this-key-exist).
### Reading a value with a fallback
!!! question
How do I read a value that may be missing?
Use [`value`](../features/element_access/default_value.md), which returns a default for a missing key. It still throws if
the key exists with a different type; see [Working without exceptions](../features/no_exceptions.md).
### Iterating and nested values
!!! question
How do I loop over an array or object, or reach a nested value?
See [Iterators](../features/iterators.md) and [`items()`](../api/basic_json/items.md) for loops, and
[JSON Pointer](../features/json_pointer.md) to address or test a nested value by path.
### Unexpected arrays when creating values
!!! question
Why does `json j{x}` or `{{"a", 1}, {"b"}}` produce an array instead of what I expected?
Braces create arrays and objects based on their content, and single-element braces create a one-element array. See
[Brace initialization yields arrays](#brace-initialization-yields-arrays) and the table in
[Creating values](../features/creating_values.md#what-does-this-produce).
### Keeping the order of object keys
!!! question
The keys of my object come out sorted. How do I keep the insertion order?
Use `ordered_json`, and call `ordered_json::parse` when reading. See [Object order](../features/object_order.md).
## Converting your own types
### Structs, enums, and optional fields
!!! question
How do I convert my own types to and from JSON?
Write `to_json`/`from_json` functions or use the `NLOHMANN_DEFINE_TYPE_*` macros; see
[Arbitrary type conversions](../features/arbitrary_types.md). Enums can be mapped to strings
([Enum conversion](../features/enum_conversion.md)), and an empty `std::optional` can be omitted
([Omitting a field](../features/conversions.md#omitting-a-field-when-serializing-stdoptional)).
### Smart pointers, variants, chrono, and class hierarchies
!!! question
Why can't I convert `std::shared_ptr`, `std::variant`, `std::chrono` types, or a pointer to a base class?
There is more than one sensible JSON representation for them, so the library leaves the choice to you.
[Recipes for common types](../features/type_recipes.md) shows one for each.
### My `to_json` is not found
!!! question
I wrote `to_json`/`from_json`, but the compiler does not find them. Why?
Typical causes are a type alias of a standard type, a function in the wrong namespace, or a declaration that comes too
late. See [Why isn't my `to_json` found?](../features/arbitrary_types.md#why-isnt-my-to_json-or-from_json-found).
## Limitations ## Limitations
### Relaxed parsing ### Relaxed parsing
@@ -203,6 +405,36 @@ Not directly, but the companion project [json-schema-validator](https://github.c
builds JSON Schema (draft 7; draft 4 in its older, now-superseded 1.x releases) validation on top of this library builds JSON Schema (draft 7; draft 4 in its older, now-superseded 1.x releases) validation on top of this library
and is a common recommendation for this use case. and is a common recommendation for this use case.
### Memory use
!!! question
Does the library leak memory? My process stays large after I free a `json` value.
Destroying a value frees everything it owns. Freed memory is returned to the allocator, which does not necessarily give
it back to the operating system, so a high resident size alone is not a leak. Copies are deep, and `clear()` keeps the
capacity of an array. See [Memory](../features/performance.md#memory), and use Valgrind or the sanitizers to tell a leak
from retained memory.
### Reproducible builds
!!! question
The header path ends up in my binary. How do I get path-independent builds?
The runtime assertions embed `__FILE__`. Define `NDEBUG` (the default for release builds) or map the path prefix; see
[Notes for builds and distribution](../integration/index.md#notes-for-builds-and-distribution).
### Slow debug builds
!!! question
Parsing a large file is very slow in my debug build. Can I compile the library with optimizations?
The library is header-only, so there is no separate source file. Keep the code that uses `json` in its own source file and
compile that file with optimizations; see
[Notes for builds and distribution](../integration/index.md#notes-for-builds-and-distribution).
## Exceptions ## Exceptions
### Parsing without exceptions ### Parsing without exceptions
@@ -211,7 +443,8 @@ and is a common recommendation for this use case.
Is it possible to indicate a parse error without throwing an exception? Is it possible to indicate a parse error without throwing an exception?
Yes, see [Parsing and exceptions](../features/parsing/parse_exceptions.md). Yes, see [Parsing and exceptions](../features/parsing/parse_exceptions.md) and
[Working without exceptions](../features/no_exceptions.md).
### Key name in exceptions ### Key name in exceptions
+4
View File
@@ -31,6 +31,10 @@ int main()
j["name"] = "Niels"; j["name"] = "Niels";
j["list"] = {1, 0, 2}; j["list"] = {1, 0, 2};
// read a value into a C++ variable and use it
double pi = j["pi"].get<double>();
std::cout << "2 * pi = " << 2 * pi << '\n';
// serialize with an indent of 4 spaces // serialize with an indent of 4 spaces
std::cout << j.dump(4) << '\n'; std::cout << j.dump(4) << '\n';
} }
+33
View File
@@ -43,6 +43,21 @@ using json = nlohmann::json;
to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and
Clang). Clang).
!!! tip "Which file do I download?"
For a project without a package manager, the library itself, including the `_json` literals, is **one file**:
[`json.hpp`](https://github.com/nlohmann/json/releases/latest/download/json.hpp) from the
[releases page](https://github.com/nlohmann/json/releases). You do not need to clone the repository, build
anything, or link a library. Put the file in a directory named `nlohmann` that is on your include path (for
example `third_party/nlohmann/json.hpp` with `-I third_party`), so that `#include <nlohmann/json.hpp>` works.
Download the companion headers listed below only if you use them: `json_view.hpp` for the
[zero-copy views](../features/json_view.md), `json_fwd.hpp` for forward declarations, and `json_literals.hpp` if
you define `JSON_NO_AUTOMATIC_UDLS`. Keep them in the same `nlohmann` directory.
Do not copy only parts of the repository's `include/` directory: the headers in it include each other and must be
kept together. If your IDE reports "cannot open source file" while the compiler succeeds, the compiler's include
path is set correctly but the IDE's is not; add the same directory to the IDE's include paths.
You can further use file You can further use file
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
for forward declarations (see [Compile times](compile_times.md)), and file for forward declarations (see [Compile times](compile_times.md)), and file
@@ -55,6 +70,24 @@ types, additionally include
[`single_include/nlohmann/json_view.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_view.hpp); [`single_include/nlohmann/json_view.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_view.hpp);
see [Zero-copy JSON views](../features/json_view.md). see [Zero-copy JSON views](../features/json_view.md).
## Notes for builds and distribution
**Reproducible and path-independent builds.** The runtime assertions use `assert`, which embeds the name of the source
file (`__FILE__`) in the binary. Release builds do not contain them, because they define `NDEBUG`. If you compile with
assertions enabled and need binaries that do not depend on the build directory, either define `NDEBUG` or map the
prefix of the path with the compiler option `-ffile-prefix-map=<old>=<new>` (GCC 8 and later, Clang 10 and later). See
[Runtime assertions](../features/assertions.md).
**There is no separate source file to compile.** The library is header-only, so there is no `.cpp` that could be
built with different options than the code that includes it. It is also not available as a precompiled library. To
compile the library with optimizations in an otherwise unoptimized build (for example to parse large files in a debug
build, which is slow because of the unoptimized code and the checks of the debug standard library), keep the code that
uses `json` in a separate source file and set the options for that file only. [Compile times](compile_times.md)
shows how to [instantiate `basic_json` once](compile_times.md#instantiate-basic_json-once).
**Debugger visualization.** The Natvis file works with the Visual Studio debugger, including the `cppvsdbg` engine in
Visual Studio Code, but not with debuggers that wrap LLDB, such as `codelldb`; see [Debugging](../home/debugging.md).
## See also ## See also
- [CMake](cmake.md) - integrate the library into a CMake project - [CMake](cmake.md) - integrate the library into a CMake project
+3
View File
@@ -106,6 +106,7 @@ nav:
- Features: - Features:
- features/index.md - features/index.md
- features/arbitrary_types.md - features/arbitrary_types.md
- features/type_recipes.md
- Binary Formats: - Binary Formats:
- features/binary_formats/index.md - features/binary_formats/index.md
- features/binary_formats/bjdata.md - features/binary_formats/bjdata.md
@@ -130,11 +131,13 @@ nav:
- features/modifying_values.md - features/modifying_values.md
- features/modules.md - features/modules.md
- 'nlohmann Namespace': features/namespace.md - 'nlohmann Namespace': features/namespace.md
- features/no_exceptions.md
- features/object_order.md - features/object_order.md
- features/performance.md - features/performance.md
- Parsing: - Parsing:
- features/parsing/index.md - features/parsing/index.md
- features/parsing/json_lines.md - features/parsing/json_lines.md
- features/parsing/large_and_partial_input.md
- features/parsing/parse_exceptions.md - features/parsing/parse_exceptions.md
- features/parsing/parser_callbacks.md - features/parsing/parser_callbacks.md
- features/parsing/sax_interface.md - features/parsing/sax_interface.md