|
JSON Voorhees
Killer JSON for C++
|
Simply put, this class is a collection of deserializer and serializer instances.
More...
#include <jsonv/serialization/formats.hpp>
Public Types | |
| using | list = std::vector< formats > |
Public Member Functions | |
| formats () | |
Create a new, empty formats instance. | |
| std::expected< void, ast_node_type > | deserialize (const std::type_info &type, reader &from, void *into, deserialization_context &context) const |
Deserialize the provided type from a reader into an area of memory. | |
| const deserializer & | get_deserializer (std::type_index type) const |
Get the deserializer for the given type. | |
| const deserializer & | get_deserializer (const std::type_info &type) const |
Get the deserializer for the given type. | |
| void | serialize (const std::type_info &type, const void *from, writer &to, const serialization_context &context) const |
| Write the object of type at from into to. | |
| value | to_json (const std::type_info &type, const void *from, const serialization_context &context) const |
Encode the provided value from into a JSON value: serialize into a value_encoder. | |
| const serializer & | get_serializer (std::type_index type) const |
Gets the serializer for the given type. | |
| const serializer & | get_serializer (const std::type_info &type) const |
Gets the serializer for the given type. | |
| void | register_deserializer (const deserializer *, duplicate_type_action action=duplicate_type_action::exception) |
Register a deserializer that lives in some unmanaged space. | |
| void | register_deserializer (std::shared_ptr< const deserializer >, duplicate_type_action action=duplicate_type_action::exception) |
Register a deserializer with shared ownership between this formats instance and anything else. | |
| void | register_serializer (const serializer *, duplicate_type_action action=duplicate_type_action::exception) |
Register a serializer that lives in some managed space. | |
| void | register_serializer (std::shared_ptr< const serializer >, duplicate_type_action action=duplicate_type_action::exception) |
Register a serializer with shared ownership between this formats instance and anything else. | |
| void | register_adapter (const adapter *, duplicate_type_action action=duplicate_type_action::exception) |
Register an adapter that lives in some unmanaged space. | |
| void | register_adapter (std::shared_ptr< const adapter >, duplicate_type_action action=duplicate_type_action::exception) |
Register an adapter with shared ownership between this formats instance and anything else. | |
| bool | operator== (const formats &other) const |
| Test for equality between this instance and other. | |
| bool | operator!= (const formats &other) const |
Test for inequality between this instance and other. The opposite of operator==. | |
| formats (const formats &)=default | |
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both. | |
| formats (formats &&) noexcept=default | |
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both. | |
| formats & | operator= (const formats &)=default |
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both. | |
| formats & | operator= (formats &&) noexcept=default |
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both. | |
Static Public Member Functions | |
| static formats | defaults () |
Get the default formats instance. | |
| static formats | global () |
Get the global formats instance. | |
| static formats | set_global (formats) |
Set the global formats instance. | |
| static formats | reset_global () |
Reset the global formats instance to defaults. | |
| static formats | coerce () |
Get the coercing formats instance. | |
| static formats | compose (const list &bases) |
Create a new (empty) formats using the bases as backing formats. | |
Simply put, this class is a collection of deserializer and serializer instances.
Ultimately, formats form a directed graph of possible types to load. This allows you to compose formats including your application with any number of 3rd party libraries an base formats. Imagine an application that has both a user facing API and an object storage system, both of which speak JSON. When loading from the database, you would like strict type enforcement – check that when you say deserialize<int>(val), that val actually has kind::integer. When getting values from the API, you happily convert kind::string with the value "51" into the integer 51. You really don't want to have to write two versions of decoders for all of your objects: one which uses the standard checked value::as_integer and one that uses coerce_integer – you want the same object model.
To do this, you would create your object model (called my_app_formats in the chart) with the object models for your application-specific types. This can use any number of 3rd party libraries to get the job done. To make a functional formats, you would compose different formats instances into a single one. From there,
Definition at line 156 of file formats.hpp.
| using jsonv::formats::list = std::vector<formats> |
Definition at line 159 of file formats.hpp.
| jsonv::formats::formats | ( | ) |
Create a new, empty formats instance.
By default, this does not know how to deserialize anything – not even the basic types like int64_t or std::string.
|
default |
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both.
Moving transfers the node and leaves the source empty: it finds no deserializer or serializer, a register_ call starts a new node, and compose adds nothing for it. A moved-from formats is equal only to another one in that state, since it is no node in the graph.
|
defaultnoexcept |
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both.
Moving transfers the node and leaves the source empty: it finds no deserializer or serializer, a register_ call starts a new node, and compose adds nothing for it. A moved-from formats is equal only to another one in that state, since it is no node in the graph.
|
static |
Get the coercing formats instance.
This uses loose type-checking and behaves by the same rules as the coerce_ functions in coerce.hpp: a string holding "10" deserializes as the integer 10, an integer deserializes as the string "5", and every kind has a truth value – kind::null's is false. It is composed over defaults, so anything it does not redefine – value and std::string_view among them – behaves as it does there, range checking included.
formats, so modifications do not affect the actual instance.
|
static |
Create a new (empty) formats using the bases as backing formats.
This forms a directed graph of formats objects. When searching for a deserializer or serializer, the formats is searched, then each base is searched depth-first left-to-right.
| bases | Is the list of formats objects to use as bases for the newly-created formats. Order matters – the bases are searched left-to-right, so formats that are farther left take precedence over those more to the right. |
formats objects, since the base of all formats are eventually empty. If there is an existing set of nodes \( k \) and each new format created with compose is in \( k+1 \), there is no way to create a link from \( k \) into \( k+1 \) and so there is no way to create a circuit.
|
static |
Get the default formats instance.
This uses strict type-checking and behaves by the same rules as the value as_ member functions (as_integer, as_string, etc): the JSON node has to be the kind the C++ type is built from.
Range is the one place the built-ins are stricter than those accessors. An integer literal which does not fit the destination is reported as a problem rather than read as a std::int64_t and then wrapped into it, so 999 is not a std::uint8_t and -1 is not a std::uint64_t.
A std::string_view is deserialized as a view of the source rather than a copy, so the source has to outlive it: the caller's value for an in-memory deserialization, the JSON text for a reader over one. A string the source spelt with escape sequences has no decoded form in it to view and is refused, as is one belonging to a source which is freed when deserialization finishes – a tree the pipeline materialised, or text handed to jsonv::deserialize to own. See deserialization_context::source_is_temporary.
formats, so modifications do not affect the actual instance. | std::expected< void, ast_node_type > jsonv::formats::deserialize | ( | const std::type_info & | type, |
| reader & | from, | ||
| void * | into, | ||
| deserialization_context & | context | ||
| ) | const |
Deserialize the provided type from a reader into an area of memory.
The context is passed to the deserializer which performs the conversion. In general, this should not be used directly as it is quite painful to do so – prefer deserialization_context::deserialize or the free function jsonv::deserialize.
This only finds the deserializer and calls it. What deserialization_context::deserialize does around that call is skipped: an exception is not recorded as a problem but propagates, and a deserialization started from a hook which can see deserialization_context::source_value or deserialization_context::encoded_source does not have them hidden.
deserializer returned; see deserializer::deserialize.| no_deserializer | if a deserializer for type could not be found. |
| const deserializer & jsonv::formats::get_deserializer | ( | const std::type_info & | type | ) | const |
Get the deserializer for the given type.
| no_deserializer | if a deserializer for type could not be found. |
| const deserializer & jsonv::formats::get_deserializer | ( | std::type_index | type | ) | const |
Get the deserializer for the given type.
| no_deserializer | if a deserializer for type could not be found. |
| const serializer & jsonv::formats::get_serializer | ( | const std::type_info & | type | ) | const |
Gets the serializer for the given type.
| no_serializer | if a serializer for type could not be found. |
| const serializer & jsonv::formats::get_serializer | ( | std::type_index | type | ) | const |
Gets the serializer for the given type.
| no_serializer | if a serializer for type could not be found. |
|
static |
Get the global formats instance.
By default, this is the same as defaults, but you can override it with set_global. The deserialize function uses this formats instance if none is provided, so this is convenient if your application only has one type of formats to use.
formats, so modifications do not affect the actual instance. If you wish to alter the global formats, use set_global. Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both.
Moving transfers the node and leaves the source empty: it finds no deserializer or serializer, a register_ call starts a new node, and compose adds nothing for it. A moved-from formats is equal only to another one in that state, since it is no node in the graph.
Copying a formats shares the node in the graph rather than duplicating it, so register_ calls through either one are seen by both.
Moving transfers the node and leaves the source empty: it finds no deserializer or serializer, a register_ call starts a new node, and compose adds nothing for it. A moved-from formats is equal only to another one in that state, since it is no node in the graph.
| bool jsonv::formats::operator== | ( | const formats & | other | ) | const |
Test for equality between this instance and other.
If two formats are equal, they are the exact same node in the graph. Even if one formats has the exact same types for the exact same deserializers. A moved-from formats is in no node, so it is equal only to another moved-from one.
| void jsonv::formats::register_adapter | ( | const adapter * | , |
| duplicate_type_action | action = duplicate_type_action::exception |
||
| ) |
Register an adapter that lives in some unmanaged space.
| duplicate_type_error | if this formats instance already has either a deserializer or serializer that serves the provided adapter::get_type and the duplicate_type_action is exception. |
| void jsonv::formats::register_adapter | ( | std::shared_ptr< const adapter > | , |
| duplicate_type_action | action = duplicate_type_action::exception |
||
| ) |
Register an adapter with shared ownership between this formats instance and anything else.
| duplicate_type_error | if this formats instance already has either a deserializer or serializer that serves the provided adapter::get_type the duplicate_type_action is exception. |
| void jsonv::formats::register_deserializer | ( | const deserializer * | , |
| duplicate_type_action | action = duplicate_type_action::exception |
||
| ) |
Register a deserializer that lives in some unmanaged space.
| duplicate_type_error | if this formats instance already has a deserializer that serves the provided deserializer::get_type and the duplicate_type_action is exception. |
| void jsonv::formats::register_deserializer | ( | std::shared_ptr< const deserializer > | , |
| duplicate_type_action | action = duplicate_type_action::exception |
||
| ) |
Register a deserializer with shared ownership between this formats instance and anything else.
| duplicate_type_error | if this formats instance already has a deserializer that serves the provided deserializer::get_type and the duplicate_type_action is exception. |
| void jsonv::formats::register_serializer | ( | const serializer * | , |
| duplicate_type_action | action = duplicate_type_action::exception |
||
| ) |
Register a serializer that lives in some managed space.
| duplicate_type_error | if this formats instance already has a serializer that serves the provided serializer::get_type and the duplicate_type_action is exception. |
| void jsonv::formats::register_serializer | ( | std::shared_ptr< const serializer > | , |
| duplicate_type_action | action = duplicate_type_action::exception |
||
| ) |
Register a serializer with shared ownership between this formats instance and anything else.
| duplicate_type_error | if this formats instance already has a serializer that serves the provided serializer::get_type and the duplicate_type_action is exception. |
|
static |
Reset the global formats instance to defaults.
| void jsonv::formats::serialize | ( | const std::type_info & | type, |
| const void * | from, | ||
| writer & | to, | ||
| const serialization_context & | context | ||
| ) | const |
Write the object of type at from into to.
The context is passed to the serializer which performs the conversion. In general, this should not be used directly as it is painful to do so – prefer serialization_context::serialize or the free function jsonv::to_json.
This only finds the serializer and calls it. What serialization_context::serialize does around that call is skipped: an exception which is not a serialization_error propagates as it was thrown rather than being wrapped in one.
| no_serializer | if a serializer for type could not be found. Its serialization_error::path is to.current_path(), where the value was wanted. |
Set the global formats instance.
| value jsonv::formats::to_json | ( | const std::type_info & | type, |
| const void * | from, | ||
| const serialization_context & | context | ||
| ) | const |
Encode the provided value from into a JSON value: serialize into a value_encoder.
In general, this should not be used directly – prefer serialization_context::to_json or the free function jsonv::to_json.
| no_serializer | if a serializer for type could not be found. |
| std::logic_error | if the serializer wrote nothing or left a structure open, as value_encoder::take does. |