JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
jsonv::extraction_context Class Reference

Provides extra information to routines used for extraction, collects the problems they encounter, and tracks where in the document they are. More...

#include <jsonv/serialization/extract.hpp>

+ Inheritance diagram for jsonv::extraction_context:
+ Collaboration diagram for jsonv::extraction_context:

Classes

class  path_scope
 An RAII guard naming one step of the extraction path while it is alive. More...
 

Public Types

using problem_list = extraction_error::problem_list
 

Public Member Functions

 extraction_context ()
 Create a new instance using the default formats (formats::global).
 
 extraction_context (jsonv::formats fmt, std::optional< jsonv::version > ver=std::nullopt, jsonv::path p=jsonv::path(), const void *userdata=nullptr, extract_options options=extract_options())
 Create a new instance using the given fmt, ver, p, userdata and options.
 
 extraction_context (const extraction_context &)=delete
 
extraction_context & operator= (const extraction_context &)=delete
 
bool source_is_temporary () const noexcept
 Is the source being extracted storage which is freed when extraction finishes, rather than storage the caller keeps?
 
const extract_options & options () const noexcept
 Get the options this context is extracting under.
 
jsonv::path path () const
 Get the path currently being extracted, as named by the live path_scope guards.
 
template<typename... TArgs>
std::unexpected< ast_node_type > problem (TArgs &&... args)
 Note that a problem has been encountered, forwarding args to an extraction_error::problem.
 
problem_list take_problems_since (problem_list::size_type mark)
 Remove and return the problems recorded since mark, a value problems() previously reported the size of.
 
void skip_failed_value (reader &from) noexcept
 Step from past the value whose failure is being recovered from.
 
void note_value_consumed (const reader &from) noexcept
 Note that the failure about to be reported has already consumed, from from, the value it failed on, so whatever recovers from it must not step over that value a second time.
 
template<typename TAstNode >
std::expected< TAstNode, ast_node_type > current_as (reader &from)
 Get the reader::current AST node of from as a TAstNode, recording a problem if it is some other type.
 
jsonv::path problem_path (const reader &from) const
 Where to report a problem noticed while from is sitting on the thing that is wrong.
 
template<typename T >
T extract (const value &from)
 Attempt to extract a T from the in-memory from using the formats associated with this context.
 
const problem_list & problems () const &
 
problem_list && problems () &&
 
bool recover () const noexcept
 
bool recover (const extraction_error &ex)
 
std::expected< void, ast_node_type > expect (reader &from, ast_node_type type)
 
std::expected< void, ast_node_type > expect (reader &from, std::initializer_list< ast_node_type > types)
 
template<typename T >
std::expected< T, ast_node_type > extract (reader &from)
 
std::expected< void, ast_node_type > extract (const std::type_info &type, reader &from, void *into)
 
- Public Member Functions inherited from jsonv::context
 context ()
 Create a new instance using the default formats (formats::global).
 
 context (jsonv::formats fmt, std::optional< jsonv::version > ver=std::nullopt, const void *userdata=nullptr)
 Create a new instance using the given fmt, ver and userdata.
 
const jsonv::formats & formats () const
 Get the formats object backing extraction and encoding.
 
const std::optional< jsonv::version > & version () const
 Get the version this context was created with.
 
const void * user_data () const
 Get a pointer to arbitrary user data.
 

Friends

class path_scope
 

Detailed Description

Provides extra information to routines used for extraction, collects the problems they encounter, and tracks where in the document they are.

Unlike a serialization_context, this is mutable and single-use: recording a problem changes it. It is neither copyable nor movable, since a path_scope holds a pointer to the instance it was pushed onto.

Definition at line 347 of file extract.hpp.

Member Typedef Documentation

◆ problem_list

using jsonv::extraction_context::problem_list = extraction_error::problem_list

Definition at line 351 of file extract.hpp.

Constructor & Destructor Documentation

◆ extraction_context()

jsonv::extraction_context::extraction_context ( jsonv::formats  fmt,
std::optional< jsonv::version >  ver = std::nullopt,
jsonv::path  p = jsonv::path(),
const void *  userdata = nullptr,
extract_options  options = extract_options() 
)
explicit

Create a new instance using the given fmt, ver, p, userdata and options.

Parameters
pA path all reported problems are relative to. This is almost always empty – it exists for extraction of a document which is itself a fragment of some larger one.
optionsWhat to do when something goes wrong. The default reports the first problem and stops; see extract_options::on_error.

Member Function Documentation

◆ current_as()

template<typename TAstNode >
std::expected< TAstNode, ast_node_type > jsonv::extraction_context::current_as ( reader &  from)
inline

Get the reader::current AST node of from as a TAstNode, recording a problem if it is some other type.

See also
expect
reader::current_as

Definition at line 532 of file extract.hpp.

◆ expect()

std::expected< void, ast_node_type > jsonv::extraction_context::expect ( reader &  from,
ast_node_type  type 
)

Check that the reader::current AST node of from has the given type or is one of the given types. If it is not, a problem describing the mismatch is recorded and the type actually found is returned.

This is reader::expect plus the human-readable message, which lives here because this is the layer that has the path and the problem list to attach it to.

See also
current_as
reader::expect

◆ extract()

template<typename T >
std::expected< T, ast_node_type > jsonv::extraction_context::extract ( reader &  from)
inline

Attempt to extract a T from from using the formats associated with this context.

This is the positioned primitive a composite calls for each of its parts: it extracts the value under the cursor and nothing else. In particular it does not step over ast_node_type::document_start, so it is not the way to start on a fresh reader – jsonv::extract is.

Template Parameters
Tis the type to extract. It must be movable.

Definition at line 566 of file extract.hpp.

+ Here is the call graph for this function:

◆ note_value_consumed()

void jsonv::extraction_context::note_value_consumed ( const reader &  from)
noexcept

Note that the failure about to be reported has already consumed, from from, the value it failed on, so whatever recovers from it must not step over that value a second time.

The value bridge says this for itself. An adapter which walks the reader has to say it whenever it fails with the value behind it rather than in front of it: after a nested extraction which succeeded, or once it has read its own closing token. A composite which fails part-way through a structure should finish walking that structure first – the position inside it means nothing to a caller – and then say so.

See also
skip_failed_value

◆ options()

const extract_options & jsonv::extraction_context::options ( ) const
inlinenoexcept

Get the options this context is extracting under.

Definition at line 394 of file extract.hpp.

◆ path()

jsonv::path jsonv::extraction_context::path ( ) const

Get the path currently being extracted, as named by the live path_scope guards.

This is built on demand by walking the scope chain, so it is not free – but nothing on a successful extraction calls it. If no scope is live the result is the base path this context was created with, which is usually empty; see path_scope for why that is not the same as "the root of the document".

◆ problem()

template<typename... TArgs>
std::unexpected< ast_node_type > jsonv::extraction_context::problem ( TArgs &&...  args)
inline

Note that a problem has been encountered, forwarding args to an extraction_error::problem.

Returns
std::unexpected of ast_node_type::error in all cases, which converts implicitly into any std::expected<T, ast_node_type>, so an implementation can simply return it:
return context.problem(context.path(), "Expected a value between 500 and 2500");
Provides extra information to routines used for extraction and serialization.
Definition context.hpp:26
An adapter for enumeration types.

Recording a problem does not throw. The entry point which started extraction throws a single extraction_error carrying everything collected, once the pipeline has unwound.

Definition at line 418 of file extract.hpp.

◆ problem_path()

jsonv::path jsonv::extraction_context::problem_path ( const reader &  from) const

Where to report a problem noticed while from is sitting on the thing that is wrong.

This is path when any path_scope has named a position and the reader's own reader::current_path when none has – never both, since an adapter walking a single reader would otherwise have its position counted twice. expect and current_as report through this; an extractor which rejects a value for a reason other than its node type – a number outside the range of what it builds, say – wants the same answer for the same reason.

It is not free: on a text-backed reader with no scope live, reader::current_path rescans from the start of the document. Ask for it when recording a problem, not before one happens.

◆ problems() [1/2]

problem_list && jsonv::extraction_context::problems ( ) &&
inline

Definition at line 429 of file extract.hpp.

◆ problems() [2/2]

const problem_list & jsonv::extraction_context::problems ( ) const &
inline

Get the problems encountered so far. If this list is empty, no problems have occurred.

Definition at line 427 of file extract.hpp.

◆ recover()

bool jsonv::extraction_context::recover ( ) const
noexcept

May extraction recover from a failure and keep going?

A composite which knows where its next element begins – the next element of an array, the next key of an object – asks this when one of them fails. A true answer means skip what failed and keep walking, so one bad element does not hide every problem after it; false means report the failure and let the pipeline unwind. Only the loop knows where it would resume, which is why collecting is something a composite opts into rather than something this context can deliver on its own – and why a failure with no enclosing composite ends extraction however extract_options::failure_mode is set.

The answer is false under extract_options::on_error::fail_immediately, and becomes false in collect_all once extract_options::max_failures problems have been recorded. It is never a promise that extraction will succeed: recovering collects diagnostics, it does not produce partial objects, so a composite which recovered from anything must still report failure once its loop is done.

auto element = context.extract<T>(from);
if (!element)
{
if (!context.recover())
return std::unexpected(element.error());
recovered = true;
context.skip_failed_value(from);
continue;
}

Note skip_failed_value rather than reader::next_value: where the value which failed was read through the value bridge, the cursor is already past it and stepping again would skip the next one.

The overload taking an extraction_error is for an adapter on the value bridge, which reports failure by throwing. On true the problems ex carries have been folded onto this context and the caller may continue; on false nothing was folded and the caller should rethrow ex, which the catch in extract(const std::type_info&, reader&, void*) folds instead. Either way every problem is recorded exactly once, which is the thing to preserve: the value -based overloads hand their problems to the exception rather than leaving them behind, so a fold in both places would report each failure twice.

◆ skip_failed_value()

void jsonv::extraction_context::skip_failed_value ( reader &  from)
noexcept

Step from past the value whose failure is being recovered from.

This is reader::next_value, except where the step has already happened. Most extractors leave the cursor naming the value they rejected, so stepping over it is exactly one reader::next_value. An adapter on the value bridge reading a structure out of JSON text is the exception: materialising that structure is what walks the cursor over it, so by the time the older body reports a failure the cursor names the next sibling. A loop recovering with a bare reader::next_value steps over that sibling as well, dropping it from the result and dropping every problem it had to report – and misnumbering everything after it.

The note this consults belongs to from and to the one failure being reported. It is cleared when the next extraction starts, so a note nobody collects expires rather than answering for an unrelated position.

See also
recover
note_value_consumed

◆ source_is_temporary()

bool jsonv::extraction_context::source_is_temporary ( ) const
inlinenoexcept

Is the source being extracted storage which is freed when extraction finishes, rather than storage the caller keeps?

An extractor which returns a view of what it was given must check this and refuse when it is true, because the storage its view would name is gone by the time the caller has it. std::string_view is the built-in one. There are two ways to get here:

  • A value -based adapter runs against a reader over JSON text. There is no pre-existing tree for it to borrow, so one is materialised and destroyed as the bridge unwinds. This is true for everything nested under such a materialisation, not only the value that caused it.
  • The source was handed to extraction to own: jsonv::extract given a std::string rvalue, or an rvalue reader which owns its source. That source dies with the call, so this is true for the whole extraction.

Definition at line 390 of file extract.hpp.

◆ take_problems_since()

problem_list jsonv::extraction_context::take_problems_since ( problem_list::size_type  mark)

Remove and return the problems recorded since mark, a value problems() previously reported the size of.

A composite which recovered still has to report failure, and on the value -based interface that means throwing an extraction_error. This is how it hands over what it collected without leaving a copy behind for the catch which folds that error back onto a context to record a second time.

Friends And Related Symbol Documentation

◆ path_scope

Definition at line 634 of file extract.hpp.


The documentation for this class was generated from the following file: