Json Algebraics with Mir
9il
ilyayaroshenko at gmail.com
Sun Apr 11 13:52:45 UTC 2021
Hi all,
As you may know, [Mir](https://github.com/libmir) provides two
JSON libraries: [asdf](http://asdf.libmir.org/) and WIP
[mir-ion](http://mir-ion.libmir.org/). The last one is based on
Amazon's Ion dual format.
Both libraries provide a direct de/serialization API that doesn't
need to have a mutable JSON value. This works awesome for almost
all cases, except when we want to work with JSON tree directly
and dynamically change it. Or sometimes a JSON value may have
different types and we want to check them at runtime.
Looking into JSON value implementations, we can find that they
are _tagged nullable self-referencing algebraic_ types.
- _algebraic_ - type can store a value of a type from a fixed
typeset.
- _self-referencing_ - algebraic typeset types can refer to this
algebraic type
- _nullable_ - `typeof(null)` type is supported, and it is the
default
- _tagged_ - we have an enumeration (`enum`) of the whole
typeset, and value has a property, which returns an enumeration
value that corresponds to the underlying type.
We can define such algebraics with
[mir.algebraic](http://mir-core.libmir.org/mir_algebraic.html):
```d
import mir.algebraic: TaggedVariant, This;
union JsonAlgebraicUnion
{
typeof(null) null_;
bool boolean;
long integer;
double float_;
immutable(char)[] string;
/// Self alias in array
This[] array;
/// Self alias in associative
This[immutable(char)[]] object;
}
alias JsonAlgebraic = TaggedVariant!JsonAlgebraicUnion;
unittest
{
JsonAlgebraic value;
JsonAlgebraic[string] object;
// Default
assert(value.isNull);
assert(value.kind == JsonAlgebraic.Kind.null_);
// Boolean
value = true;
object["key"] = value;
assert(!value.isNull);
assert(value == true);
assert(value.kind == JsonAlgebraic.Kind.boolean);
assert(value.get!bool == true);
assert(value.get!(JsonAlgebraic.Kind.boolean) == true);
...
```
We added serialization of
[mir.algebraic](http://mir-core.libmir.org/mir_algebraic.html) a
few months ago to both JSON libraries. It is effortless to
implement: send a serialization lambda to a visitor. And the
typeset isn't limited to JSON-like types (bool, string, double,
etc.). If all types of the typeset are serializable, then
algebraic is serializable as well.
Deserialization is more complicated. We need to define a rule of
how we want to deserialize a JSON type set to an algebraic
typeset. And more, we don't need to provide a unique
JsonAlgebraic type.
Instead, we provide a common JSON algebraic alias
[mir.algebraic_alias.json](http://mir-algorithm.libmir.org/mir_algebraic_alias_json.html) and support user-provided algebraic aliases as well.
For example, user-provided types can be:
```d
import mir.algebraic: Variant, Nullable, This;
struct Color { ubyte a, r, g, b; }
alias JsonValue0 = Variant!(string, Color[], This[string]);
alias JsonValue1 = Nullable!(This[], long);
```
Asdf match Json types according to the following rules:
- `typeof(null)` can handle JSON `null`
- `bool` can handle JSON `true` and `false`
- `string` can handle JSON strings
- `double` can handle JSON numbers
- `long` can handle JSON integer numbers and has priority for
them comparing to `double`
- `T[]` can handle JSON arrays, where T is a deserializable
type, including the algebraic itself
- `StringMap!T` and `T[string]` can handle JSON objects, where T
is a deserializable type, including the algebraic type itself.
[StringMap](http://mir-algorithm.libmir.org/mir_string_map.html)
is an ordered string-value associative array with fast search
operations. It has a priority over built-in associative arrays.
- Other types of algebraic typeset aren't used for
deserialization.
- If no type can handle the current JSON value, then an
exception is thrown.
Users APIs will be consistent even if they will define different,
their own JSON algebraic aliases. Mir algebraic types:
- are order-independent: `Variant!(A, B)` is the same type as
`Variant!(B, A)`
- can be constructed from their algebraic subset.
- can get their algebraic subset.
mir-ion support for algebraic deserialization will be added later
and extended with Ion types, including Blob, Clob, Timestamp,
Decimal, and 4 bytes floats.
Kind regards,
Ilya
---
This work has been sponsored by Symmetry Investments and Kaleidic
Associates.
More information about the Digitalmars-d-announce
mailing list