Reorganize directories (#3462)

* 🚚 move files
* 🚚 rename doc folder to docs
* 🚚 rename test folder to tests
This commit is contained in:
Niels Lohmann
2022-05-01 09:41:50 +02:00
committed by GitHub
parent 5352856f04
commit b21c345179
865 changed files with 52 additions and 51 deletions
@@ -0,0 +1,13 @@
# Parsing
!!! note
This page is under construction.
## Input
## SAX vs. DOM parsing
## Exceptions
See [parsing and exceptions](parse_exceptions.md).
@@ -0,0 +1,49 @@
# JSON Lines
The [JSON Lines](https://jsonlines.org) format is a text format of newline-delimited JSON. In particular:
1. The input must be UTF-8 encoded.
2. Every line must be a valid JSON value.
3. The line separator must be `\n`. As `\r` is silently ignored, `\r\n` is also supported.
4. The final character may be `\n`, but is not required to be one.
!!! example "JSON Text example"
```json
{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
{"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]}
{"name": "May", "wins": []}
{"name": "Deloise", "wins": [["three of a kind", "5♣"]]}
```
JSON Lines input with more than one value is treated as invalid JSON by the [`parse`](../../api/basic_json/parse.md) or
[`accept`](../../api/basic_json/accept.md) functions. To process it line by line, functions like
[`std::getline`](https://en.cppreference.com/w/cpp/string/basic_string/getline) can be used:
!!! example "Example: Parse JSON Text input line by line"
The example below demonstrates how JSON Lines can be processed.
```cpp
--8<-- "examples/json_lines.cpp"
```
Output:
```json
--8<-- "examples/json_lines.output"
```
!!! warning "Note"
Using [`operator>>`](../../api/basic_json/operator_gtgt.md) like
```cpp
json j;
while (input >> j)
{
std::cout << j << std::endl;
}
```
with a JSON Lines input does not work, because the parser will try to parse one value after the last one.
@@ -0,0 +1,114 @@
# Parsing and Exceptions
When the input is not valid JSON, an exception of type [`parse_error`](../../home/exceptions.md#parse-errors) 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](../../home/exceptions.md#parse-errors). In case you process untrusted input, always enclose your code with a `#!cpp try`/`#!cpp catch` block, like
```cpp
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:
## Switch off exceptions
The `parse()` function accepts as last parameter a `#!cpp bool` variable `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`).
```cpp
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.
```cpp
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](sax_interface.md) and decide what should happen in case of a parse error.
This function has the following interface:
```cpp
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
```cpp
#include <iostream>
#include "json.hpp"
using json = nlohmann::json;
class sax_no_exception : public nlohmann::detail::json_sax_dom_parser<json>
{
public:
sax_no_exception(json& j)
: nlohmann::detail::json_sax_dom_parser<json>(j, false)
{}
bool parse_error(std::size_t position,
const std::string& last_token,
const json::exception& ex)
{
std::cerr << "parse error at input byte " << position << "\n"
<< ex.what() << "\n"
<< "last read: \"" << last_token << "\""
<< std::endl;
return false;
}
};
int main()
{
std::string myinput = "[1,2,3,]";
json result;
sax_no_exception sax(result);
bool parse_result = json::sax_parse(myinput, &sax);
if (!parse_result)
{
std::cerr << "parsing unsuccessful!" << std::endl;
}
std::cout << "parsed value: " << result << std::endl;
}
```
Output:
```
parse error at input byte 8
[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
last read: "3,]"
parsing unsuccessful!
parsed value: [1,2,3]
```
@@ -0,0 +1,79 @@
# Parser Callbacks
## Overview
With a parser callback function, the result of parsing a JSON text can be influenced. When passed to `parse`, it is called on certain events
(passed as `parse_event_t` via parameter `event`) with a set recursion depth `depth` and context JSON value `parsed`. The return value of the
callback function is a boolean indicating whether the element that emitted the callback shall be kept or not.
The type of the callback function is:
```cpp
template<typename BasicJsonType>
using parser_callback_t =
std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
```
## Callback event types
We distinguish six scenarios (determined by the event type) in which the callback function can be called. The following table describes the values
of the parameters `depth`, `event`, and `parsed`.
| parameter `event` | description | parameter `depth` | parameter `parsed` |
|-------------------------------|-----------------------------------------------------------|-------------------------------------------|----------------------------------|
| `parse_event_t::object_start` | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded |
| `parse_event_t::key` | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key |
| `parse_event_t::object_end` | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object |
| `parse_event_t::array_start` | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded |
| `parse_event_t::array_end` | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array |
| `parse_event_t::value` | the parser finished reading a JSON value | depth of the value | the parsed JSON value |
??? example
When parsing the following JSON text,
```json
{
"name": "Berlin",
"location": [
52.519444,
13.406667
]
}
```
these calls are made to the callback function:
| event | depth | parsed |
| -------------- | ----- | ------ |
| `object_start` | 0 | *discarded* |
| `key` | 1 | `#!json "name"` |
| `value` | 1 | `#!json "Berlin"` |
| `key` | 1 | `#!json "location"` |
| `array_start` | 1 | *discarded* |
| `value` | 2 | `#!json 52.519444` |
| `value` | 2 | `#!json 13.406667` |
| `array_end` | 1 | `#!json [52.519444,13.406667]` |
| `object_end` | 0 | `#!json {"location":[52.519444,13.406667],"name":"Berlin"}` |
## Return value
Discarding a value (i.e., returning `#!c false`) has different effects depending on the context in which function was called:
- Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never read.
- In case a value outside a structured type is skipped, it is replaced with `#!json null`. This case happens if the top-level element is skipped.
??? example
The example below demonstrates the `parse()` function with and without callback function.
```cpp
--8<-- "examples/parse__string__parser_callback_t.cpp"
```
Output:
```json
--8<-- "examples/parse__string__parser_callback_t.output"
```
@@ -0,0 +1,73 @@
# SAX Interface
The library uses a SAX-like interface with the following functions:
```plantuml
interface json::sax_t {
+ {abstract} bool null()
+ {abstract} bool boolean(bool val)
+ {abstract} bool number_integer(number_integer_t val)
+ {abstract} bool number_unsigned(number_unsigned_t val)
+ {abstract} bool number_float(number_float_t val, const string_t& s)
+ {abstract} bool string(string_t& val)
+ {abstract} bool binary(binary_t& val)
+ {abstract} bool start_object(std::size_t elements)
+ {abstract} bool end_object()
+ {abstract} bool start_array(std::size_t elements)
+ {abstract} bool end_array()
+ {abstract} bool key(string_t& val)
+ {abstract} bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex)
}
```
```cpp
// called when null is parsed
bool null();
// called when a boolean is parsed; value is passed
bool boolean(bool val);
// called when a signed or unsigned integer number is parsed; value is passed
bool number_integer(number_integer_t val);
bool number_unsigned(number_unsigned_t val);
// called when a floating-point number is parsed; value and original string is passed
bool number_float(number_float_t val, const string_t& s);
// called when a string is parsed; value is passed and can be safely moved away
bool string(string_t& val);
// called when a binary value is parsed; value is passed and can be safely moved away
bool binary(binary& val);
// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
bool start_object(std::size_t elements);
bool end_object();
bool start_array(std::size_t elements);
bool end_array();
// called when an object key is parsed; value is passed and can be safely moved away
bool key(string_t& val);
// called when a parse error occurs; byte position, the last token, and an exception is passed
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex);
```
The return value of each function determines whether parsing should proceed.
To implement your own SAX handler, proceed as follows:
1. Implement the SAX interface in a class. You can use class `nlohmann::json_sax<json>` as base class, but you can also use any class where the functions described above are implemented and public.
2. Create an object of your SAX interface class, e.g. `my_sax`.
3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
Note the `sax_parse` function only returns a `#!cpp bool` indicating the result of the last executed SAX event. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`.
## See also
- [json_sax](../../api/json_sax/index.md) - documentation of the SAX interface
- [sax_parse](../../api/basic_json/sax_parse.md) - SAX parser