|
JSON Voorhees
Killer JSON for C++
|
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON ast_node sequence. More...
#include <jsonv/reader.hpp>
Public Member Functions | |
| reader (parse_index index) | |
| Create a reader which reads from the given index. | |
| reader (const reader &)=delete | |
| reader & | operator= (const reader &)=delete |
| bool | good () const |
| Check if this reader is still good to read from. | |
| void | validate () const |
| Check that the source this reader was created from is valid JSON. | |
| bool | owns_source () const noexcept |
| Does this reader own the storage it reads from? | |
| const ast_node & | current () const |
| Get the current AST node this reader is pointing at. | |
| ast_node_type | current_type () const |
Get the type of the current AST node, which is always current().type(). | |
| template<typename TAstNode > | |
| std::expected< TAstNode, ast_node_type > | current_as () const |
Get the current AST node as a specific TAstNode subtype, calling expect beforehand. | |
| optional< const value & > | current_value () const noexcept |
Get the in-memory value this reader is positioned on, if it has one to lend. | |
| const path & | current_path () const |
| Get the path to the current node this reader is pointing at. | |
| bool | next_token () noexcept |
| Go to the next token. | |
| bool | next_structure () noexcept |
| Go to one past the end of the current structure. | |
| bool | next_value () noexcept |
| Go to one past the value this reader is on. | |
| bool | next_key () |
| Go to the next object key or end-of-object. | |
| reader (std::string_view source) | |
| Create a reader which reads from JSON source. | |
| reader (std::string_view source, const parse_options &parse_options) | |
| Create a reader which reads from JSON source. | |
| reader (const char *source) | |
| Create a reader which reads from JSON source. | |
| reader (const char *source, const parse_options &parse_options) | |
| Create a reader which reads from JSON source. | |
| reader (std::string &&source) | |
| Create a reader which reads from JSON source. | |
| reader (std::string &&source, const parse_options &parse_options) | |
| Create a reader which reads from JSON source. | |
| reader (reader &&) noexcept | |
Moving a reader transfers its implementation, leaving the source moved-from: good is false and the accessors throw std::invalid_argument. | |
| reader & | operator= (reader &&) noexcept |
Moving a reader transfers its implementation, leaving the source moved-from: good is false and the accessors throw std::invalid_argument. | |
| std::expected< void, ast_node_type > | expect (ast_node_type type) const |
Check that the current AST node has the given type or is one of the expected types. | |
| std::expected< void, ast_node_type > | expect (std::initializer_list< ast_node_type > types) const |
Check that the current AST node has the given type or is one of the expected types. | |
Static Public Member Functions | |
| static reader | from_value (const value &value) |
| Create a reader which reads from an in-memory value. | |
| static reader | from_value (value &&value) |
| Create a reader which reads from an in-memory value. | |
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON ast_node sequence.
Readers normalize access to JSON source for conversion to some other format. They can be provided with pre-parsed JSON through a parse_index or value. They can be provided with a std::string or std::string_view directly. This allows extractor implementations to operate on all forms of JSON without worrying about the implementation.
A reader is a forward cursor over that sequence. It starts on ast_node_type::document_start, so the first thing to do is step onto the value itself – which jsonv::extract does for you when handed a fresh reader. Reading an object means walking its keys, handling the ones you recognize and skipping the ones you do not:
Note that next_key is only valid while sitting on a key or on the opening { – it is the "skip this member" step, not the loop's advance. Use next_token to move off a value you have just read.
Definition at line 103 of file reader.hpp.
|
explicit |
Create a reader which reads from JSON source.
The source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to a std::string, which is moved to the reader's implementation to keep it alive. It is parsed with parse_options where they are given, and with the defaults parse_index::parse uses where they are not.
|
explicit |
Create a reader which reads from JSON source.
The source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to a std::string, which is moved to the reader's implementation to keep it alive. It is parsed with parse_options where they are given, and with the defaults parse_index::parse uses where they are not.
|
explicit |
Create a reader which reads from JSON source.
The source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to a std::string, which is moved to the reader's implementation to keep it alive. It is parsed with parse_options where they are given, and with the defaults parse_index::parse uses where they are not.
|
explicit |
Create a reader which reads from JSON source.
The source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to a std::string, which is moved to the reader's implementation to keep it alive. It is parsed with parse_options where they are given, and with the defaults parse_index::parse uses where they are not.
|
explicit |
Create a reader which reads from JSON source.
The source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to a std::string, which is moved to the reader's implementation to keep it alive. It is parsed with parse_options where they are given, and with the defaults parse_index::parse uses where they are not.
|
explicit |
Create a reader which reads from JSON source.
The source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to a std::string, which is moved to the reader's implementation to keep it alive. It is parsed with parse_options where they are given, and with the defaults parse_index::parse uses where they are not.
|
noexcept |
Moving a reader transfers its implementation, leaving the source moved-from: good is false and the accessors throw std::invalid_argument.
These are out-of-line because destroying the implementation needs a complete reader::impl, which this header does not have – the same reason the destructor is.
| const ast_node & jsonv::reader::current | ( | ) | const |
Get the current AST node this reader is pointing at.
| std::logic_error | if this instance is not good, or std::invalid_argument if it has been moved-from. |
|
inline |
Get the current AST node as a specific TAstNode subtype, calling expect beforehand.
current node as a TAstNode; otherwise the ast_node_type the current node actually has. | std::logic_error | if this instance is not good, or std::invalid_argument if it has been moved-from. |
Definition at line 224 of file reader.hpp.
| const path & jsonv::reader::current_path | ( | ) | const |
Get the path to the current node this reader is pointing at.
This is used in the generation of error messages to describe the location of something that could not be extracted.
| std::logic_error | if this instance is not good, or std::invalid_argument if it has been moved-from. |
| ast_node_type jsonv::reader::current_type | ( | ) | const |
Get the type of the current AST node, which is always current().type().
Use this where the type is all that is wanted – checking for the ] which ends an array, say. A reader over a value has to synthesise token text for a number, a string or a key before it can hand out an ast_node, and answering this does not.
| std::logic_error | if this instance is not good, or std::invalid_argument if it has been moved-from. |
Get the in-memory value this reader is positioned on, if it has one to lend.
A reader created by from_value is walking a value which already exists, so the subtree under current is something it can hand out by reference rather than rebuild. A reader over JSON text has no such tree.
Extraction uses this to avoid copying a subtree it was already given, and so that an extractor which returns a view of what it was handed – std::string_view among them – borrows the caller's storage rather than a temporary which dies with the call.
current names; or nothing if this reader is not value-backed, is not good, or is positioned somewhere which does not start a value, such as an object key or a closing token. | std::expected< void, ast_node_type > jsonv::reader::expect | ( | ast_node_type | type | ) | const |
Check that the current AST node has the given type or is one of the expected types.
current node matches type or one of the given types; otherwise the ast_node_type the current node actually has. | std::invalid_argument | if types is empty. |
| std::logic_error | if this instance is not good, or std::invalid_argument if it has been moved-from. |
| std::expected< void, ast_node_type > jsonv::reader::expect | ( | std::initializer_list< ast_node_type > | types | ) | const |
Check that the current AST node has the given type or is one of the expected types.
current node matches type or one of the given types; otherwise the ast_node_type the current node actually has. | std::invalid_argument | if types is empty. |
| std::logic_error | if this instance is not good, or std::invalid_argument if it has been moved-from. |
Create a reader which reads from an in-memory value.
| value | The value to read from. The overload taking a reference does not copy it, so it must remain valid for the lifetime of the reader; the rvalue overload moves value into the reader, which then keeps it alive. |
These are named rather than constructors because value converts implicitly from std::string, among others. A reader(const value&) constructor would make reader(some_std_string) ambiguous – a std::string reaches std::string_view and value through user-defined conversions of equal rank – and the same trap would reopen for every type value grows a converting constructor from. Returning by value costs nothing: the result is a prvalue of the returned type, so it initializes the caller's object directly without a move.
Create a reader which reads from an in-memory value.
| value | The value to read from. The overload taking a reference does not copy it, so it must remain valid for the lifetime of the reader; the rvalue overload moves value into the reader, which then keeps it alive. |
These are named rather than constructors because value converts implicitly from std::string, among others. A reader(const value&) constructor would make reader(some_std_string) ambiguous – a std::string reaches std::string_view and value through user-defined conversions of equal rank – and the same trap would reopen for every type value grows a converting constructor from. Returning by value costs nothing: the result is a prvalue of the returned type, so it initializes the caller's object directly without a move.
| bool jsonv::reader::good | ( | ) | const |
Check if this reader is still good to read from.
This will be true if this instance has not been moved-from and has not reached EOF. If this is false, current or current_path will throw an exception.
| bool jsonv::reader::next_key | ( | ) |
Go to the next object key or end-of-object.
true if the reader is still good to read from current. | std::invalid_argument | if the reader is not currently at the start of a key. |
|
noexcept |
Go to one past the end of the current structure.
The point of this over next_token is abandoning a structure you are only part-way through. Once the one member you came for has been read, there is no reason to walk the rest of the object:
Note the call site: next_structure is used while sitting on a value inside the object, which is where it differs from next_token. Called on the closing } itself it is merely next_token, since there is no longer a structure to leave.
true if the reader is still good to read from current.
|
noexcept |
Go to the next token.
true if the reader is still good to read from current.
|
noexcept |
Go to one past the value this reader is on.
Unlike next_structure, which leaves the structure the reader is inside, this steps over the single value the reader is on. On a structure that means the token after its matching close, since the structure is the value being stepped over; on anything else it is the same as next_token.
This is the primitive for ignoring a value you do not want. Note the difference from next_structure at the 4 above: this goes to the }, while next_structure leaves the enclosing object entirely.
true if the reader is still good to read from current. Moving a reader transfers its implementation, leaving the source moved-from: good is false and the accessors throw std::invalid_argument.
These are out-of-line because destroying the implementation needs a complete reader::impl, which this header does not have – the same reason the destructor is.
|
noexcept |
Does this reader own the storage it reads from?
This is true for a reader created from a std::string rvalue or by from_value(value&&), which keep their source alive for exactly as long as the reader. Anything extracted as a view of the source – a std::string_view, say – is then valid only while the reader is. It is false for every other source, where the caller owns the storage, and for a moved-from reader.
| void jsonv::reader::validate | ( | ) | const |
Check that the source this reader was created from is valid JSON.
Parsing text never throws: a malformed document produces a sequence which stops at an ast_node_type::error node, and a reader walks it as far as it goes. What was wrong is not on that node in any form a reader of it can act on, so this is the question parse_index::validate answers, asked of the reader. It is about the source, not the cursor, so the answer is the same wherever the reader is positioned.
| parse_error | if this reader is over JSON text which did not parse. A reader over a value has nothing to parse and never throws this. |
| std::invalid_argument | if this instance has been moved-from. |