mirror of
https://github.com/nlohmann/json.git
synced 2026-11-02 10:55:35 +00:00
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
@@ -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)
|
||||
Reference in new issue
Block a user