The library aims to provide an extremely convient way to serialize and deserialize objects, leveraging static reflection to provide a simple interface.
Consider a normal type MyType
which has static reflection added. This can be serialized as follows:
The library also supports deserialization using a mostly symmetric interface. For example:
Using the JSON serializer, enumerations can also be serialized to strings using static reflection. Additionally, users can provide custom serialization behavior by overriding the di::serialize_metadata
function. For instance:
This allows overriding the names or only serializing a subset of the fields. This can be done on a per-format basis or for all formats at once. When resolving how to serialize a type, the following order is used:
di::serialize_metadata
overload for the given format, use that.di::serialize_metadata
overload, use that.di::reflect
overload, use that.This metadata search is handled by the di::serialize
function, which evaluates the metadata and then calls the provided serializer. This dispatching mechanism ensures that serializers will have consistent behavior across all implementations.
Additionally, di::serialize()
is a tag-invoke CPO, so the entire serialization mechanism can be customized on a per-type basis if needed.
For example,
The deserialization mechanism has equivalent customization points, like di::deserialize_metadata
and di::deserialize
. However, they take di::InPlaceType<T>
as arguments instead of the actual value to be serialized. Additionally, di::deserialization_metadata
will fallback to di::serialize_metadata
if no deserialization metadata is provided.
The library is designed to be extensible to support multiple serialization formats in the future. A serialization format is modelled by the di::SerializationFormat
concept, which is defined as follows:
As shown, a serialization format must provide an implementation of the serializer
function which takes a writer, and returns a bound serializer. This can be done by providing a static function serializer
. This should be valid for any type of writer, but since c++ concepts cannot universality, a type-erased Writer is checked. The serializer must be a type which models the di::Serializer
concept, which is defined as follows:
The metadata object passed to the serialization is either a list of fields or a atom, depending on the type being serialized. Different serializers may be able to serialize different values, so the second constraint on Serializer
is not enforced. Instead, the important concept is one that considers both the serializer and the type being serialized:
The deserialization mechanism is similar to the serialization mechanism, but with a few key differences. First, the di::Deserializer
concept replaces the writer()
function with a reader()
function. Second, the di::Deserializer
concept is not constrained by the SerializationFormat
concept. Instead, it is constrained by the DeserializationFormat
concept, which is defined as follows:
To be a useful di::Deserializer
, the type must overload the deserialize()
function to accept an InPlaceType<T>
to be deserialized. Optionally, it can also accept a metadata object for the type being deserialized.