The OpenD Programming Language

mir.ion.examples

DRAFT

Examples

A user may define setter and/or getter properties.

import mir.ser.json;
import mir.deser.json;
import mir.conv: to;

static struct S
{
    @serdeIgnore string str;
@safe pure:
    string a() const @property
    {
        return str;
    }

    void b(int s) @property
    {
        str = s.to!string;
    }
}

assert(S("str").serializeJson == `{"a":"str"}`);
assert(`{"b":123}`.deserializeJson!S.str == "123");

Support for custom nullable types (types that has a bool property isNull, non-void property get returning payload and void property nullify that makes nullable type to null value)

1 import mir.ser.json;
2 import mir.deser.json;
3 
4 static struct MyNullable
5 {
6     long value;
7 
8 @safe pure nothrow @nogc:
9 
10     @property
11     isNull() const
12     {
13         return value == 0;
14     }
15 
16     @property
17     long get() const
18     {
19         return value;
20     }
21 
22     @property
23     nullify()
24     {
25         value = 0;
26     }
27 
28     auto opAssign(long value)
29     {
30         this.value = value;
31     }
32 }
33 
34 static struct Foo
35 {
36     MyNullable my_nullable;
37     string field;
38 
39     bool opEquals()(auto ref const(typeof(this)) rhs) const
40     {
41         if (my_nullable.isNull && rhs.my_nullable.isNull)
42             return field == rhs.field;
43 
44         if (my_nullable.isNull != rhs.my_nullable.isNull)
45             return false;
46 
47         return my_nullable == rhs.my_nullable &&
48                      field == rhs.field;
49     }
50 }
51 
52 Foo foo;
53 foo.field = "it's a foo";
54 
55 assert (serializeJson(foo) == `{"my_nullable":null,"field":"it's a foo"}`);
56 
57 foo.my_nullable = 200;
58 
59 assert (deserializeJson!Foo(`{"my_nullable":200,"field":"it's a foo"}`) == Foo(MyNullable(200), "it's a foo"));
60 
61 import mir.algebraic: Nullable;
62 
63 static struct Bar
64 {
65     Nullable!long nullable;
66     string field;
67 
68     bool opEquals()(auto ref const(typeof(this)) rhs)
69     {
70         if (nullable.isNull && rhs.nullable.isNull)
71             return field == rhs.field;
72 
73         if (nullable.isNull != rhs.nullable.isNull)
74             return false;
75 
76         return nullable == rhs.nullable &&
77                      field == rhs.field;
78     }
79 }
80 
81 Bar bar;
82 bar.field = "it's a bar";
83 
84 assert (serializeJson(bar) == `{"nullable":null,"field":"it's a bar"}`);
85 
86 bar.nullable = 777;
87 assert (deserializeJson!Bar(`{"nullable":777,"field":"it's a bar"}`) == Bar(Nullable!long(777), "it's a bar"));
import mir.ser.ion;
import mir.ser.json;
import mir.ion.stream;

IonValueStream[string] map;

map["num"] = IonValueStream(serializeIon(124));
map["str"] = IonValueStream(serializeIon("value"));

auto json = map.serializeJson;
assert(json == `{"str":"value","num":124}` || json == `{"num":124,"str":"value"}`);

Support for floating point nan and (partial) infinity

import mir.conv: to;
import mir.ser.ion;
import mir.ser.json;
import mir.deser.json;
import mir.ion.conv;

static struct Foo
{
    float f;

    bool opEquals()(auto ref const(typeof(this)) rhs)
    {
        return  f != f && rhs.f != rhs.f || f == rhs.f;
    }
}

// test for Not a Number
assert (serializeJson(Foo()) == `{"f":"nan"}`, serializeJson(Foo()));
assert (serializeIon(Foo()).ion2json == `{"f":"nan"}`, serializeIon(Foo()).ion2json);

assert (deserializeJson!Foo(`{"f":"nan"}`) == Foo(), deserializeJson!Foo(`{"f":"nan"}`).to!string);

assert (serializeJson(Foo(1f/0f)) == `{"f":"+inf"}`);
assert (serializeIon(Foo(1f/0f)).ion2json == `{"f":"+inf"}`);
assert (deserializeJson!Foo(`{"f":"+inf"}`)  == Foo( float.infinity));
assert (deserializeJson!Foo(`{"f":"-inf"}`) == Foo(-float.infinity));

assert (serializeJson(Foo(-1f/0f)) == `{"f":"-inf"}`);
assert (serializeIon(Foo(-1f/0f)).ion2json == `{"f":"-inf"}`);
assert (deserializeJson!Foo(`{"f":"-inf"}`) == Foo(-float.infinity));
import mir.ser.ion;
import mir.ser.json;
import mir.deser.json;
import mir.ion.conv;

static struct S
{
    string foo;
    uint bar;
}

static immutable json = `{"foo":"str","bar":4}`;
assert(serializeIon(S("str", 4)).ion2json == json);
assert(serializeJson(S("str", 4)) == json);
assert(deserializeJson!S(json) == S("str", 4));

Proxy for members

import mir.ser.json;
import mir.deser.json;

struct S
{
    // const(char)[] doesn't reallocate ASDF data.
    @serdeProxy!(const(char)[])
    uint bar;
}

auto json = `{"bar":"4"}`;
assert(serializeJson(S(4)) == json);
assert(deserializeJson!S(json) == S(4));
import mir.ser.json;
import mir.deser.json;
import mir.serde: serdeKeys;
static struct S
{
    @serdeKeys("b", "a")
    string s;
}
assert(`{"a":"d"}`.deserializeJson!S.serializeJson == `{"b":"d"}`);
import mir.ser.json;
import mir.deser.json;
import mir.serde: serdeKeys, serdeKeyOut;
static struct S
{
    @serdeKeys("a")
    @serdeKeyOut("s")
    string s;
}
assert(`{"a":"d"}`.deserializeJson!S.serializeJson == `{"s":"d"}`);
import mir.ser.json;
import mir.deser.json;
import std.exception: assertThrown;

struct S
{
    string field;
}

assert(`{"field":"val"}`.deserializeJson!S.field == "val");
// assertThrown(`{"other":"val"}`.deserializeJson!S);
import mir.ser.json;
import mir.deser.json;

static struct S
{
    @serdeKeyOut("a")
    string s;
}
assert(`{"s":"d"}`.deserializeJson!S.serializeJson == `{"a":"d"}`);
import mir.ser.json;
import mir.deser.json;
import std.exception: assertThrown;

static struct S
{
    @serdeIgnore
    string s;
}
// assertThrown(`{"s":"d"}`.deserializeJson!S);
assert(S("d").serializeJson == `{}`);
1 import mir.ser.json;
2 import mir.deser.json;
3 
4 static struct Decor
5 {
6     int candles; // 0
7     float fluff = float.infinity; // inf 
8 }
9 
10 static struct Cake
11 {
12     @serdeIgnoreDefault string name = "Chocolate Cake";
13     @serdeIgnoreDefault string nullStr;
14     @serdeIgnoreDefault string emptyStr = "";
15     int slices = 8;
16     float flavor = 1;
17     @serdeIgnoreDefault
18     Decor dec = Decor(20); // { 20, inf }
19 }
20 
21 assert(Cake("Normal Cake", "", null).serializeJson == `{"name":"Normal Cake","slices":8,"flavor":1.0}`);
22 auto cake = Cake.init;
23 cake.dec = Decor.init;
24 assert(cake.serializeJson == `{"slices":8,"flavor":1.0,"dec":{"candles":0,"fluff":"+inf"}}`);
25 assert(cake.dec.serializeJson == `{"candles":0,"fluff":"+inf"}`);
26 
27 static struct A
28 {
29     @serdeIgnoreDefault
30     string str = "Banana";
31     int i = 1;
32 }
33 assert(A.init.serializeJson == `{"i":1}`);
34 
35 static struct S
36 {
37     @serdeIgnoreDefault
38     A a;
39 }
40 assert(S.init.serializeJson == `{}`);
41 assert(S(A("Berry")).serializeJson == `{"a":{"str":"Berry","i":1}}`);
42 
43 static struct D
44 {
45     S s;
46 }
47 assert(D.init.serializeJson == `{"s":{}}`);
48 assert(D(S(A("Berry"))).serializeJson == `{"s":{"a":{"str":"Berry","i":1}}}`);
49 assert(D(S(A(null, 0))).serializeJson == `{"s":{"a":{"str":"","i":0}}}`);
50 
51 static struct F
52 {
53     D d;
54 }
55 assert(F.init.serializeJson == `{"d":{"s":{}}}`);
import mir.ser.json;
import mir.deser.json;
import mir.serde: serdeIgnoreOut;

static struct S
{
    @serdeIgnoreOut
    string s;
}
assert(`{"s":"d"}`.deserializeJson!S.s == "d");
assert(S("d").serializeJson == `{}`);
import mir.ser.json;
import mir.deser.json;

static struct CustomType
{
    int a;

    // can be part of a type as well (only omits on property keys)
    auto serdeIgnoreOut() const
    {
        return a < 0;
    }

    // and use custom serializer to serialize as int
    void serialize(S)(scope ref S serializer) scope const
    {
        import mir.ser : serializeValue;

        serializeValue(serializer, a);
    }
}

static struct S
{
    @serdeIgnoreOutIf!`a < 0`
    int a;

    // b is not output if the serdeIgnoreOut property in its type evaluates to false.
    CustomType b;
}

assert(serializeJson(S(3, CustomType(2))) == `{"a":3,"b":2}`, serializeJson(S(3, CustomType(2))));
assert(serializeJson(S(-3, CustomType(-2))) == `{}`);
import mir.ser.json;
import mir.deser.json;

import std.uuid: UUID;

static struct S
{
    @serdeScoped
    @serdeProxy!string
    UUID id;
}

enum result = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46");
assert(`{"id":"8AB3060E-2cba-4f23-b74c-b52db3bdfb46"}`.deserializeJson!S.id == result);
1 import mir.ser.json;
2 import mir.deser.json;
3 import mir.ion.value;
4 import mir.algebraic: Variant;
5 
6 static struct ObjectA
7 {
8     string name;
9 }
10 static struct ObjectB
11 {
12     double value;
13 }
14 
15 alias MyObject = Variant!(ObjectA, ObjectB);
16 
17 static struct MyObjectArrayProxy
18 {
19     MyObject[] array;
20 
21     this(inout(MyObject)[] array) @safe pure nothrow @nogc inout
22     {
23         this.array = array;
24     }
25 
26     inout(T) opCast(T : MyObject[])() inout
27     {
28         return array;
29     }
30 
31     void serialize(S)(scope ref S serializer) scope const
32     {
33         import mir.ser: serializeValue;
34         // mir.algebraic has builtin support for serialization.
35         // For other algebraic libraies one can use thier visitor handlers.
36         serializeValue(serializer, array);
37     }
38 
39     /++
40     Returns: error msg if any
41     +/
42     @trusted pure scope
43     IonException deserializeFromIon(scope const char[][] symbolTable, scope IonDescribedValue value)
44     {
45         import mir.deser.ion: deserializeIon;
46         import mir.ion.exception;
47         foreach (IonErrorCode error, scope IonDescribedValue elem; value.get!IonList)
48         {
49             if (error)
50                 return error.ionException;
51             array ~= "name" in elem.get!IonStruct.withSymbols(symbolTable)
52                 ? MyObject(deserializeIon!ObjectA(symbolTable, elem))
53                 : MyObject(deserializeIon!ObjectB(symbolTable, elem));
54         }
55         return null;
56     }
57 }
58 
59 static struct SomeObject
60 {
61     @serdeProxy!MyObjectArrayProxy MyObject[] objects;
62 }
63 
64 string data = q{{"objects":[{"name":"test"},{"value":1.5}]}};
65 
66 auto value = data.deserializeJson!SomeObject;
67 assert (value.serializeJson == data, value.serializeJson);

reflectSerde support

import mir.reflection: reflectSerde;
static struct S
{
    @reflectSerde enum s = "str";
}

import mir.ser.text: serializeText;
assert(S().serializeText == `{s:"str"}`);

serdeIgnoreUnexpectedKeys support

import mir.serde: serdeIgnoreUnexpectedKeys;
@serdeIgnoreUnexpectedKeys
static struct S { int i; }

import mir.deser.text: deserializeText;
assert(`{s:"str",i:4}`.deserializeText!S == S(4));

Iterable and serdeLikeList

import mir.deser.text: deserializeText;
import mir.ser.text: serializeText;
import mir.serde: serdeIgnoreOut, serdeLikeList, serdeProxy, serdeKeys;
import std.array: Appender;

static struct S
{
    @serdeLikeList
    @serdeProxy!string // input element type of
    @serdeIgnoreOut
    @serdeKeys("strings")
    Appender!(string[]) stringsApp; //`put` method is used

    this(const(string)[] strings)
    {
        stringsApp.put(strings);
    }

    auto strings() const @property // Iterable
    {
        import mir.ndslice.topology: map;
        return stringsApp.data.map!((string s) => "_" ~ s);
    }

}

assert(S([`a`, `b`]).serializeText == `{strings:["_a","_b"]}`);
import mir.test;
`{strings:["a","b"]}`.deserializeText!S.stringsApp.data.should == [`a`, `b`];

@serdeLikeList
@serdeProxy!string // input element type of
static struct C
{
    /// Used to make S deserializable
    Appender!(string[]) stringsApp; //`put` method is used
    alias stringsApp this;

    this(const(string)[] strings)
    {
        stringsApp.put(strings);
    }

    /// To make C iterable and serializable
    auto opIndex() const { return stringsApp.data; }
}

static struct S2 {
    static prop() @property { return "100"; };
}

assert(S2().serializeText == `{prop:"100"}`, S2().serializeText);
assert(C([`a`, `b`]).serializeText == `["a","b"]`);
assert(`["a","b"]`.deserializeText!C.stringsApp.data == [`a`, `b`]);

serdeLikeStruct

1 import mir.test: should;
2 import mir.ser.text: serializeText;
3 import mir.deser.text: deserializeText;
4 import mir.serde: serdeLikeStruct, serdeProxy;
5 
6 // opApply for serialization & and opIndexAssign for deserialization
7 static struct M
8 {
9     private int sum;
10 
11     // opApply is used for serialization
12     int opApply(scope int delegate(scope const char[] key, ref const int val) pure @safe dg) const pure @safe
13     {
14         { int var = 1; if (auto r = dg("a", var)) return r; }
15         { int var = 2; if (auto r = dg("b", var)) return r; }
16         { int var = 3; if (auto r = dg("c", var)) return r; }
17         return 0;
18     }
19 
20     // opIndexAssign for deserialization
21     void opIndexAssign(int val, string key) pure @safe
22     {
23         sum += val;
24     }
25 }
26 
27 // attribute on member + opApply
28 static struct S
29 {
30     @serdeLikeStruct
31     @serdeProxy!int // for deserialization
32     M obj;
33 }
34 
35 assert(S().serializeText == `{obj:{a:1,b:2,c:3}}`);
36 `{obj:{a:1,b:2,c:3}}`.deserializeText!S.obj.sum.should == 6;
37 
38 // attribute on type + opApply
39 @serdeLikeStruct
40 @serdeProxy!int // for deserialization
41 static struct C
42 {
43     M obj;
44     alias obj this;
45 }
46 
47 assert(C().serializeText == `{a:1,b:2,c:3}`);
48 assert(`{a:1,b:2,c:3}`.deserializeText!C.obj.sum == 6);
49 
50 // byKeyValue
51 static struct D
52 {
53     static struct KeyValue
54     {
55         string key;
56         int value;
57     }
58     KeyValue[] byKeyValue = [KeyValue("a", 1), KeyValue("b", 2), KeyValue("c", 3)];
59 }
60 
61 // attribute on member + byKeyValue
62 static struct F
63 {
64     @serdeLikeStruct
65     D obj;
66 }
67 
68 assert(F().serializeText == `{obj:{a:1,b:2,c:3}}`);
69 
70 // attribute on type + byKeyValue
71 @serdeLikeStruct
72 static struct B
73 {
74     D obj;
75     alias obj this;
76 }
77 
78 assert(B().serializeText == `{a:1,b:2,c:3}`);
import mir.ser.json;
import mir.deser.json;
import std.range;
import std.algorithm;
import std.conv;

static struct S
{
    @serdeTransformIn!"a += 2"
    @serdeTransformOut!(a =>"str".repeat.take(a).joiner("_").to!string)
    int a;
}

auto s = deserializeJson!S(`{"a":3}`);
assert(s.a == 5);
assert(serializeJson(s) == `{"a":"str_str_str_str_str"}`);
static struct Toto  
{
    import mir.ndslice.slice: Slice;

    Slice!(int*, 2) a;
    Slice!(int*, 1) b;

    this(int x) @safe pure nothrow
    {
        import mir.ndslice.topology: iota, repeat;
        import mir.ndslice.allocation: slice;

        a = [2, 3].iota!int.slice;
        b = repeat(x, [2]).slice;
    }
}

auto toto = Toto(5);

import mir.ser.json: serializeJson;
import mir.deser.json: deserializeJson;

auto description = toto.serializeJson!Toto;
assert(description == q{{"a":[[0,1,2],[3,4,5]],"b":[5,5]}});
assert(toto == description.deserializeJson!Toto);
static struct Toto  
{
    import mir.ndslice.slice: Slice;
    import mir.rc.array: RCI;

    Slice!(RCI!int, 2) a;
    Slice!(RCI!int, 1) b;

    this(int x) @safe pure nothrow @nogc
    {
        import mir.ndslice.topology: iota, repeat;
        import mir.ndslice.allocation: rcslice;

        a = [2, 3].iota!int.rcslice;
        b = repeat(x, [2]).rcslice;
    }
}

auto toto = Toto(5);

import mir.ser.json: serializeJson;
import mir.deser.json: deserializeJson;
import mir.format: stringBuf;

auto buffer = stringBuf;
serializeJson(buffer, toto);
auto description = buffer.data;
assert(description == q{{"a":[[0,1,2],[3,4,5]],"b":[5,5]}});
assert(toto == description.deserializeJson!Toto);

User defined algebraic types deserialization supports any subset of the following types:

  • typeof(null)
  • bool
  • long
  • double
  • string
  • $(GREF_ALTTEXT mir-algorithm, $(TT Timestamp), Timestamp, mir, timestamp)$(NBSP)
  • AnyType[]
  • StringMap!AnyType
  • AnyType[string]

A StringMap has has priority over builtin associative arrays.

Serializations works with any algebraic types.

See_also: $(GMREF mir-core, mir,algebraic), $(GMREF mir-algorithm, mir,string_map)

import mir.string_map;
import mir.deser.ion: deserializeIon;
import mir.ion.conv: json2ion, ion2text;
import mir.algebraic: Algebraic, This;

static union Json_
{
    typeof(null) null_;
    bool boolean;
    long integer;
    immutable(char)[] string;
    double[] array;
    StringMap!This object;
}

alias MyJsonAlgebraic = Algebraic!Json_;

auto json = `{"b" : true, "z" : null, "this" : {"c" : "str", "d" : [1, 2, 3, 4]}}`;
auto binary = json.json2ion;
auto value = binary.deserializeIon!MyJsonAlgebraic;

auto object = value.get!(StringMap!MyJsonAlgebraic);
assert(object["b"].get!bool == true);
assert(object["z"].isNull);

object = object["this"].get!(StringMap!MyJsonAlgebraic);
assert(object["c"].get!string == "str");
assert(object["d"].get!(double[]) == [1.0, 2, 3, 4]);
import mir.algebraic_alias.json;
import mir.ser.json: serializeJson;
auto value = [JsonAlgebraic[].init.JsonAlgebraic, StringMap!JsonAlgebraic.init.JsonAlgebraic, string.init.JsonAlgebraic];
assert(value.serializeJson == `[[],{},""]`, value.serializeJson);
import mir.test;
import mir.serde : serdeFallbackStruct;
import mir.algebraic;
import mir.deser.text;
import mir.deser.json;
@serdeFallbackStruct struct S { string path; }
alias V = Variant!(string, S);
static immutable res = [V("str"), V(S("root"))];
q{["str", {path: root}]}.deserializeText!(V[]).should == res;
q{ [ "str" , { "path" : "root" } ] }.deserializeJson!(V[]).should == res;

Date serialization

import mir.date;
import mir.ion.conv: ion2text;
import mir.ser.ion: serializeIon;
import mir.ser.json: serializeJson;
import mir.ser.text: serializeText;
assert(Date(2021, 4, 24).serializeIon.ion2text == `2021-04-24`);
assert(Date(2021, 4, 24).serializeText == `2021-04-24`);
assert(Date(2021, 4, 24).serializeJson == `"2021-04-24"`);

Timestamp and LOB support in algebraic types

import mir.algebraic;
import mir.deser.ion: deserializeIon;
import mir.ser.ion: serializeIon;
import mir.lob;
import mir.string_map;
import mir.timestamp;

static union Ion_
{
    typeof(null) null_;
    bool boolean;
    long integer;
    double float_;
    immutable(char)[] string;
    Blob blob;
    Clob clob;
    Timestamp timestamp;
    This[] array;
    StringMap!This object;
}

alias IonAlgebraic = Algebraic!Ion_;

StringMap!IonAlgebraic map;
map["ts"] = Timestamp(2021, 4, 24);
map["clob"] = Clob("Some clob");
map["blob"] = Blob([0x32, 0x52]);

assert(map.serializeIon.deserializeIon!(StringMap!IonAlgebraic) == map);

Phobos date-time serialization

import core.time : hnsecs, minutes;
import mir.ion.conv: ion2text;
import mir.ser.ion: serializeIon;
import mir.ser.json: serializeJson;
import mir.ser.text: serializeText;
import mir.deser.ion: deserializeIon;
import mir.deser.json: deserializeJson;
import std.datetime.date : Date, DateTime;
import std.datetime.systime : SysTime;
import std.datetime.timezone : SimpleTimeZone;

auto date = Date(2021, 4, 24);
assert(date.serializeIon.ion2text == `2021-04-24`);
assert(date.serializeText == `2021-04-24`);
assert(date.serializeJson == `"2021-04-24"`);
assert(`"2021-04-24"`.deserializeJson!Date == date);

auto datetime = DateTime(1982, 4, 1, 20, 59, 22);
assert(datetime.serializeIon.ion2text == `1982-04-01T20:59:22-00:00`);
assert(datetime.serializeText == `1982-04-01T20:59:22-00:00`);
assert(datetime.serializeJson == `"1982-04-01T20:59:22-00:00"`);
assert(`"1982-04-01T20:59:22Z"`.deserializeJson!DateTime == datetime);

auto dt = DateTime(1982, 4, 1, 20, 59, 22);
auto tz = new immutable SimpleTimeZone(-330.minutes);
auto st = SysTime(dt, 1234567.hnsecs, tz);
assert(st.serializeIon.ion2text == `1982-04-01T20:59:22.1234567-05:30`);
assert(st.serializeText == `1982-04-01T20:59:22.1234567-05:30`);
assert(st.serializeJson == `"1982-04-01T20:59:22.1234567-05:30"`);
assert(st.serializeIon.deserializeIon!SysTime == st);
assert(`"1982-04-01T20:59:22.1234567-05:30"`.deserializeJson!SysTime == st);

Static array support

import mir.ser.json;
import mir.deser.json;
int[3] value = [1, 2, 3];
assert(`[1,2,3]`.deserializeJson!(int[3]) == value);
assert(value.serializeJson == `[1,2,3]`);

ditto

import mir.ser.json;
import mir.deser.json;
static struct S { int v; }
S[3] value = [S(1), S(2), S(3)];
auto text = `[{"v":1},{"v":2},{"v":3}]`;
assert(text.deserializeJson!(S[3]) == value);
assert(value.serializeJson == text);
auto json = q{{
    "a": [
        0.0031
    ],
    "b": [
        0.999475,
        0.999425
    ]
}};
import mir.deser.json;
static struct S {
    double[] a;

    void serdeUnexpectedKeyHandler(scope const(char)[] key) scope @safe pure nothrow @nogc
    {
        assert(key == "b");
    }
}
auto value = json.deserializeJson!(double[][string]);
auto partialStruct = json.deserializeJson!S;
auto json = q{{
    "index": 0.9962125,
    "data": 0.0001
}};

import mir.deser.json;
import mir.series;

alias T = Observation!(double, double);
auto value = json.deserializeJson!T;
assert(value.index == 0.9962125);
assert(value.data == 0.0001);
1     static class MyHugeRESTString
2     {
3         string s;
4 
5         this(string s)  @safe pure nothrow @nogc
6         {
7             this.s = s;
8         }
9 
10         void serialize(S)(scope ref S serializer) scope const
11         {
12             auto state = serializer.stringBegin;
13             // putStringPart is usefull in the loop and with buffers
14             serializer.putStringPart(s);
15             serializer.putStringPart(" Another chunk.");
16             serializer.stringEnd(state);
17         }
18 @safe const scope pure nothrow @nogc:
19 
20         bool opEquals(scope const typeof(this) rhs)
21         {
22             return s == rhs.s;
23         }
24 
25         int opCmp(scope const typeof(this) rhs)
26         {
27             return __cmp(s, rhs.s);
28         }
29 
30         override size_t toHash()
31         {
32             return hashOf(s);
33         }
34     }
35 
36     import mir.algebraic: Algebraic, This;
37     import mir.string_map;
38 
39     // Your JSON DOM Type
40     static union Json_
41     {
42         typeof(null) null_;
43         bool boolean;
44         long integer;
45         double float_;
46         immutable(char)[] string;
47         MyHugeRESTString restString; 
48         This[] array;
49         StringMap!This object;
50     }
51     alias Json = Algebraic!Json_;
52 
53     /// ordered
54     StringMap!Json response;
55     response["type"] = Json("response");
56     response["data"] = Json(new MyHugeRESTString("First chunk."));
57 
58     import mir.ion.conv: ion2text;
59     import mir.ser.ion;
60     import mir.ser.json;
61     import mir.ser.text;
62 
63     assert(response.serializeJson == `{"type":"response","data":"First chunk. Another chunk."}`);
64     assert(response.serializeText == `{type:"response",data:"First chunk. Another chunk."}`);
65     assert(response.serializeIon.ion2text == `{type:"response",data:"First chunk. Another chunk."}`);

Float proxy for integer types

import mir.deser.json;
static struct S {
    @serdeProxy!double
    long integralValue;
}

assert(`{"integralValue":1.23e+2}`.deserializeJson!S.integralValue == 123);

Annotated variant

import mir.algebraic;
import mir.deser.json;
import mir.deser.text;
import mir.ser.json;
import mir.ser.text;

@serdeAlgebraicAnnotation("s1")
static struct S1 {
    string key;
}

@serdeAlgebraicAnnotation("s2")
static struct S2 {
    long key;
}

alias V = Variant!(S1, S2);
auto v = [V(123), V("124")];

auto ion = `[s2::{key:123},s1::{key:"124"}]`;
auto json = `[{"s2":{"key":123}},{"s1":{"key":"124"}}]`;
assert(v.serializeText == ion);
assert(ion.deserializeText!(V[2]) == v);
assert(v.serializeJson == json);
assert(json.deserializeJson!(V[2]) == v);

Annotated arrays

import mir.algebraic;
import mir.deser.ion;
import mir.ser.ion;
import mir.ser.text;
import mir.serde;

@serdeAlgebraicAnnotation("annotation")
@serdeProxy!(long[])
static struct St3 {
    long[] arr;
    alias arr this;
}
alias V = Variant!St3;

assert(V(St3([123])).serializeText == `annotation::[123]`);
assert(V(St3([123])).serializeIon.deserializeIon!V == St3([123]));

Meta