* 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>
5.2 KiB
Parsing and Exceptions
When the input is not valid JSON, an exception of type parse_error is thrown.
This exception contains the position in the input where the error occurred, together with a diagnostic message and the
last read input token. The exceptions page contains a
list of examples for parse error exceptions. In case you process untrusted
input, always enclose your code with a #!cpp try/#!cpp catch block, like
json j;
try
{
j = json::parse(my_input);
}
catch (json::parse_error& ex)
{
std::cerr << "parse error at byte " << ex.byte << std::endl;
}
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
The parse() function accepts a #!cpp bool parameter allow_exceptions which
controls whether an exception is thrown when a parse error occurs (#!cpp true, default) or whether a discarded value
should be returned (#!cpp false).
json j = json::parse(my_input, nullptr, false);
if (j.is_discarded())
{
std::cerr << "parse error" << std::endl;
}
Note there is no diagnostic information available in this scenario.
Use accept() function
Alternatively, function accept() can be used which does not return a json value,
but a #!cpp bool indicating whether the input is valid JSON.
if (!json::accept(my_input))
{
std::cerr << "parse error" << std::endl;
}
Again, there is no diagnostic information available.
User-defined SAX interface
Finally, you can implement the SAX interface and decide what should happen in case of a parse error.
This function has the following interface:
bool parse_error(std::size_t position,
const std::string& last_token,
const json::exception& ex);
The return value indicates whether the parsing should continue, so the function should usually return #!cpp false.
??? example "Example: report parse errors without exceptions"
The example derives from the library's DOM parser and overrides `parse_error` to print the error instead of
throwing. Note the DOM parser is an implementation detail (`nlohmann::detail`) and may change between releases;
see [Do not use the `detail` namespace](../../integration/migration_guide.md#do-not-use-the-detail-namespace).
```cpp
--8<-- "examples/sax_no_exception.cpp"
```
Output:
```
--8<-- "examples/sax_no_exception.output"
```
Where did the error occur?
The message of a parse_error 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. 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 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() and end_pos():
??? 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, 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.