Docs: fill documentation gaps found in user questions (#5907)
* 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> * docs: declare range-for lists in variables to keep astyle quiet astyle reformats a braced init list placed directly in a range-for header; declare the list first so check_amalgamation passes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
1 parent
cc917d7123
commit
ffd4242dc2
45 files changed
+1670
-8
No files matched your search
@@ -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`).
|
||||
|
||||
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.
|
||||
|
||||
??? 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.
|
||||
- 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 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
## 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
|
||||
|
||||
### 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
|
||||
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
|
||||
|
||||
### 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?
|
||||
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user