
// Generated by atdd from type definitions in everything.atd.
// This implements classes for the types defined in 'everything.atd', providing
// methods and functions to convert data from/to JSON.

// ############################################################################
// # Private functions
// ############################################################################

module everything_atd;

import std.algorithm : map;
import std.array : array;
import std.conv;
import std.format;
import std.json;
import std.sumtype;
import std.traits : isCallable, ReturnType;
import std.typecons : nullable, Nullable, tuple, Tuple;

private
{

  class AtdException : Exception
  {
      @safe this(string msg, string file = __FILE__, size_t line = __LINE__)
      {
          super(msg, file, line);
      }
  }

  // workaround to make toDelegate callable from safe
  @trusted auto toDelegate(F)(auto ref F fp) if (isCallable!F)
  {
    import std.functional;
    return std.functional.toDelegate(fp);
  }

  
  @trusted T _atd_missing_json_field(T)(string typeName, string jsonFieldName)
  {
      throw new AtdException("missing field %s in JSON object of type %s".format(jsonFieldName, typeName));
  }
  
  auto _atd_bad_json(T)(string expectedType, T jsonValue)
  {
      string valueStr = jsonValue.to!string;
      if (valueStr.length > 200)
      {
          valueStr = valueStr[0 .. 200];
      }
  
      return new AtdException(
          "incompatible JSON value where type '%s' was expected: %s".format(
              expectedType, valueStr
      ));
  }
  
  auto _atd_bad_d(T)(string expectedType, T jsonValue)
  {
      string valueStr = jsonValue.to!string;
      if (valueStr.length > 200)
      {
          valueStr = valueStr[0 .. 200];
      }
  
      return new AtdException(
          "incompatible D value where type '%s' was expected: %s".format(
              expectedType, valueStr
      ));
  }
  
  auto _atd_read_unit(JSONValue x)
  {
      if (x.isNull)
          return null;
      else
          throw _atd_bad_json("unit", x);
  }
  
  auto _atd_read_bool(JSONValue x)
  {
      try
          return x.boolean;
      catch (JSONException e)
          throw _atd_bad_json("bool", x);
  }
  
  auto _atd_read_int(JSONValue x)
  {
      try
          return cast(int) x.integer;
      catch (JSONException e)
          throw _atd_bad_json("int", x);
  }
  
  auto _atd_read_float(JSONValue x)
  {
      try
          return cast(float) x.floating;
      catch (JSONException e)
          throw _atd_bad_json("float", x);
  }
  
  auto _atd_read_string(JSONValue x)
  {
      try
          return x.str;
      catch (JSONException e)
          throw _atd_bad_json("string", x);
  }
  
  template _atd_read_list(alias readElements)
    {
        auto _atd_read_list(JSONValue jsonVal)
        {
            if (jsonVal.type != JSONType.array)
                throw _atd_bad_json("array", jsonVal);
            auto list = jsonVal.array;
            return array(list.map!readElements());
        }
    }

    template _atd_read_object_to_assoc_array(alias readValue)
    {
        auto _atd_read_object_to_assoc_array(JSONValue jsonVal)
        {
            alias T = ReturnType!readValue;

            if (jsonVal.type != JSONType.object)
                throw _atd_bad_json("object", jsonVal);
            T[string] ret;
            foreach (key, val; jsonVal.object)
                ret[key] = readValue(val);
            return ret;
        }
    }

    template _atd_read_array_to_assoc_dict(alias readKey, alias readValue)
    {
        auto _atd_read_array_to_assoc_dict(JSONValue jsonVal)
        {
            alias K = ReturnType!readKey;
            alias V = ReturnType!readValue;

            if (jsonVal.type != JSONType.array)
                throw _atd_bad_json("list", jsonVal);
            V[K] ret;
            foreach (jsonInnerVal; jsonVal.array)
            {
                if (jsonInnerVal.type != JSONType.array)
                    throw _atd_bad_json("list", jsonInnerVal);
                ret[readKey(jsonInnerVal[0])] = readValue(jsonInnerVal[1]);
            }
            return ret;
        }
    }

    template _atd_read_object_to_tuple_list(alias readValue)
    {
        auto _atd_read_object_to_tuple_list(JSONValue jsonVal)
        {
            alias T = ReturnType!readValue;

            if (jsonVal.type != JSONType.object)
                throw _atd_bad_json("object", jsonVal);
            auto tupList = new Tuple!(string, T)[](jsonVal.object.length);
            int i = 0;
            foreach (key, val; jsonVal.object)
                tupList[i++] = tuple(key, readValue(val));
            return tupList;
        }
    }

    template _atd_read_nullable(alias readElm)
    {
        auto _atd_read_nullable(JSONValue e)
        {
            alias T = ReturnType!readElm;

            if (e.isNull)
                return Nullable!T.init;
            else
                return Nullable!T(readElm(e));
        }
    }

    template _atd_read_option(alias readElm)
    {
        auto _atd_read_option(JSONValue e)
        {
            alias T = ReturnType!readElm;

            if (e.type == JSONType.string && e.str == "None")
                return Nullable!T.init;
            else if (e.type == JSONType.array && e.array.length == 2 && e[0].type == JSONType.string && e[0].str == "Some")
                return Nullable!T(readElm(e[1]));
            else
                throw _atd_bad_json("option", e);
        }
    }

    template _atd_read_wrap(alias readElm, alias wrap)
    {
        auto _atd_read_wrap(JSONValue e)
        {
            return wrap(readElm(e));
        }
    }

    // this whole set of function could be remplaced by one templated _atd_write_value function
    // not sure it is what we want though

    auto _atd_write_unit(typeof(null) n)
    {
        return JSONValue(null);
    }

    auto _atd_write_bool(bool b)
    {
        return JSONValue(b);
    }

    auto _atd_write_int(int i)
    {
        return JSONValue(i);
    }

    auto _atd_write_float(float f)
    {
        return JSONValue(f);
    }

    auto _atd_write_string(string s)
    {
        return JSONValue(s);
    }

    template _atd_write_list(alias writeElm)
    {
        auto _atd_write_list(T)(T[] list)
        {
            return JSONValue(array(list.map!writeElm()));
        }
    }

    template _atd_write_assoc_array_to_object(alias writeValue)
    {
        auto _atd_write_assoc_array_to_object(T)(T[string] assocArr)
        {
            JSONValue[string] ret;
            foreach (key, val; assocArr)
                ret[key] = writeValue(val);
            return JSONValue(ret);
        }
    }

    template _atd_write_assoc_dict_to_array(alias writeKey, alias writeValue)
    {
        auto _atd_write_assoc_dict_to_array(K, V)(V[K] assocArr)
        {
            JSONValue[] ret;
            foreach (key, val; assocArr)
                ret ~= JSONValue([writeKey(key), writeValue(val)]);
            return JSONValue(ret);
        }
    }

    template _atd_write_tuple_list_to_object(alias writeValue)
    {
        auto _atd_write_tuple_list_to_object(T)(Tuple!(string, T)[] tupList)
        {
            JSONValue[string] ret;
            foreach (tup; tupList)
                ret[tup[0]] = writeValue(tup[1]);
            return JSONValue(ret);
        }
    }

    template _atd_write_nullable(alias writeElm)
    {
        auto _atd_write_nullable(T)(Nullable!T elm)
        {
            if (elm.isNull)
                return JSONValue(null);
            else
                return writeElm(elm.get);
        }
    }

    template _atd_write_option(alias writeElm)
    {
        auto _atd_write_option(T)(Nullable!T elm)
        {
            if (elm.isNull)
                return JSONValue("None");
            else
                return JSONValue([JSONValue("Some"), writeElm(elm.get)]);
        }
    }

    template _atd_write_wrap(alias writeElm, alias unwrap)
    {
        auto _atd_write_wrap(Wrapped)(Wrapped e)
        {
            return writeElm(unwrap(e));
        }
    }
}

// ############################################################################
// # Public classes
// ############################################################################

auto fromJsonString(T)(string s)
{
    JSONValue res = parseJSON(s);
    return res.fromJson!T;
}

auto toJsonString(T)(T obj)
{
    JSONValue res = obj.toJson!T;
    return res.toString;
}

  


import std.stdint : uint32_t, uint16_t;


struct RecursiveClass {
    int id;
    bool flag;
    RecursiveClass[] children;
}

@trusted RecursiveClass fromJson(T : RecursiveClass)(JSONValue x) {
    RecursiveClass obj;
    obj.id = ("id" in x) ? _atd_read_int(x["id"]) : _atd_missing_json_field!(typeof(obj.id))("RecursiveClass", "id");
    obj.flag = ("flag" in x) ? _atd_read_bool(x["flag"]) : _atd_missing_json_field!(typeof(obj.flag))("RecursiveClass", "flag");
    obj.children = ("children" in x) ? _atd_read_list!(fromJson!RecursiveClass)(x["children"]) : _atd_missing_json_field!(typeof(obj.children))("RecursiveClass", "children");
    return obj;
}
@trusted JSONValue toJson(T : RecursiveClass)(T obj) {
    JSONValue res;
    res["id"] = _atd_write_int(obj.id);
    res["flag"] = _atd_write_bool(obj.flag);
    res["children"] = _atd_write_list!(((RecursiveClass x) => x.toJson!(RecursiveClass)))(obj.children);
    return res;
}


struct St{int _data; alias _data this;
this(int init) @safe {_data = init;}
this(St init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : St)(St e) {
    return _atd_write_int(e);
}
@trusted St fromJson(T : St)(JSONValue e) {
    return St(_atd_read_int(e));
}


// Original type: shape = [ ... | Circle of ... | ... ]
struct Circle { float value; }
@trusted JSONValue toJson(T : Circle)(T e) {
    return JSONValue(["Circle": _atd_write_float(e.value)]);
}


// Original type: shape = [ ... | Square of ... | ... ]
struct Square { float value; }
@trusted JSONValue toJson(T : Square)(T e) {
    return JSONValue(["Square": _atd_write_float(e.value)]);
}


// Original type: shape = [ ... | Point | ... ]
struct Point {}
@trusted JSONValue toJson(T : Point)(T e) {
    return JSONValue("Point");
}


struct Shape{ SumType!(Circle, Square, Point) _data; alias _data this;
@safe this(T)(T init) {_data = init;} @safe this(Shape init) {_data = init._data;}}

@trusted Shape fromJson(T : Shape)(JSONValue x) {
    if (x.type == JSONType.string) {
        if (x.str == "Point") 
            return Shape(Point());
        throw _atd_bad_json("Shape", x);
    }
    if (x.type == JSONType.object && x.object.length == 1) {
        string cons = x.object.keys[0];
        if (cons == "Circle")
            return Shape(Circle(_atd_read_float(x["Circle"])));
        if (cons == "Square")
            return Shape(Square(_atd_read_float(x["Square"])));
        throw _atd_bad_json("Shape", x);
    }
    throw _atd_bad_json("Shape", x);
}

@trusted JSONValue toJson(T : Shape)(T x) {
    return x.match!(
    (Circle v) => v.toJson!(Circle),
(Square v) => v.toJson!(Square),
(Point v) => v.toJson!(Point)
    );
}


// Original type: kind = [ ... | Root | ... ]
struct Root_ {}
@trusted JSONValue toJson(T : Root_)(T e) {
    return JSONValue("Root");
}


// Original type: kind = [ ... | Thing of ... | ... ]
struct Thing { int value; }
@trusted JSONValue toJson(T : Thing)(T e) {
    return JSONValue([JSONValue("Thing"), _atd_write_int(e.value)]);
}


// Original type: kind = [ ... | WOW | ... ]
struct WOW {}
@trusted JSONValue toJson(T : WOW)(T e) {
    return JSONValue("wow");
}


// Original type: kind = [ ... | Amaze of ... | ... ]
struct Amaze { string[] value; }
@trusted JSONValue toJson(T : Amaze)(T e) {
    return JSONValue([JSONValue("!!!"), _atd_write_list!(_atd_write_string)(e.value)]);
}


struct Kind{ SumType!(Root_, Thing, WOW, Amaze) _data; alias _data this;
@safe this(T)(T init) {_data = init;} @safe this(Kind init) {_data = init._data;}}

@trusted Kind fromJson(T : Kind)(JSONValue x) {
    if (x.type == JSONType.string) {
        if (x.str == "Root") 
            return Kind(Root_());
        if (x.str == "wow") 
            return Kind(WOW());
        throw _atd_bad_json("Kind", x);
    }
    if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) {
        string cons = x[0].str;
        if (cons == "Thing")
            return Kind(Thing(_atd_read_int(x[1])));
        if (cons == "!!!")
            return Kind(Amaze(_atd_read_list!(_atd_read_string)(x[1])));
        throw _atd_bad_json("Kind", x);
    }
    throw _atd_bad_json("Kind", x);
}

@trusted JSONValue toJson(T : Kind)(T x) {
    return x.match!(
    (Root_ v) => v.toJson!(Root_),
(Thing v) => v.toJson!(Thing),
(WOW v) => v.toJson!(WOW),
(Amaze v) => v.toJson!(Amaze)
    );
}


struct Alias3{uint32_t _data; alias _data this;
this(uint32_t init) @safe {_data = init;}
this(Alias3 init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : Alias3)(Alias3 e) {
    return _atd_write_wrap!(_atd_write_int, (uint32_t e) => to!int(e))(e);
}
@trusted Alias3 fromJson(T : Alias3)(JSONValue e) {
    return Alias3(_atd_read_wrap!(_atd_read_int, (int e) => to!uint32_t(e))(e));
}


struct AliasOfAliasNotWrapped{Alias3 _data; alias _data this;
this(Alias3 init) @safe {_data = init;}
this(AliasOfAliasNotWrapped init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : AliasOfAliasNotWrapped)(AliasOfAliasNotWrapped e) {
    return ((Alias3 x) => x.toJson!(Alias3))(e);
}
@trusted AliasOfAliasNotWrapped fromJson(T : AliasOfAliasNotWrapped)(JSONValue e) {
    return AliasOfAliasNotWrapped(fromJson!Alias3(e));
}


struct AliasOfAliasOfAlias{AliasOfAliasNotWrapped _data; alias _data this;
this(AliasOfAliasNotWrapped init) @safe {_data = init;}
this(AliasOfAliasOfAlias init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : AliasOfAliasOfAlias)(AliasOfAliasOfAlias e) {
    return ((AliasOfAliasNotWrapped x) => x.toJson!(AliasOfAliasNotWrapped))(e);
}
@trusted AliasOfAliasOfAlias fromJson(T : AliasOfAliasOfAlias)(JSONValue e) {
    return AliasOfAliasOfAlias(fromJson!AliasOfAliasNotWrapped(e));
}


struct Alias{int[] _data; alias _data this;
this(int[] init) @safe {_data = init;}
this(Alias init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : Alias)(Alias e) {
    return _atd_write_list!(_atd_write_int)(e);
}
@trusted Alias fromJson(T : Alias)(JSONValue e) {
    return Alias(_atd_read_list!(_atd_read_int)(e));
}


struct KindParametrizedTuple{Tuple!(Kind, Kind, int) _data; alias _data this;
this(Tuple!(Kind, Kind, int) init) @safe {_data = init;}
this(KindParametrizedTuple init) @safe {_data = init._data;}
this(T...)(T args) @safe {_data = tuple(args);}
}
@trusted JSONValue toJson(T : KindParametrizedTuple)(KindParametrizedTuple e) {
    return ((Tuple!(Kind, Kind, int) x) => JSONValue([((Kind x) => x.toJson!(Kind))(x[0]), ((Kind x) => x.toJson!(Kind))(x[1]), _atd_write_int(x[2])]))(e);
}
@trusted KindParametrizedTuple fromJson(T : KindParametrizedTuple)(JSONValue e) {
    return KindParametrizedTuple(((JSONValue x) @trusted { 
    if (x.type != JSONType.array || x.array.length != 3)
      throw _atd_bad_json("Tuple of size 3", x);
    return tuple(fromJson!Kind(x[0]), fromJson!Kind(x[1]), _atd_read_int(x[2]));
  })(e));
}


struct IntFloatParametrizedRecord {
    int field_a;
    float[] field_b = [];
}

@trusted IntFloatParametrizedRecord fromJson(T : IntFloatParametrizedRecord)(JSONValue x) {
    IntFloatParametrizedRecord obj;
    obj.field_a = ("field_a" in x) ? _atd_read_int(x["field_a"]) : _atd_missing_json_field!(typeof(obj.field_a))("IntFloatParametrizedRecord", "field_a");
    obj.field_b = ("field_b" in x) ? _atd_read_list!(_atd_read_float)(x["field_b"]) : [];
    return obj;
}
@trusted JSONValue toJson(T : IntFloatParametrizedRecord)(T obj) {
    JSONValue res;
    res["field_a"] = _atd_write_int(obj.field_a);
    res["field_b"] = _atd_write_list!(_atd_write_float)(obj.field_b);
    return res;
}


struct Root {
    string id;
    bool await;
    int integer;
    float x___init__;
    float float_with_auto_default = 0.0;
    float float_with_default = 0.1;
    int[][] items;
    Nullable!int maybe;
    int[] extras = [];
    int answer = 42;
    Alias aliased;
    Tuple!(float, float) point;
    Kind kind;
    Kind[] kinds;
    Tuple!(float, int)[] assoc1;
    Tuple!(string, int)[] assoc2;
    int[float] assoc3;
    int[string] assoc4;
    Nullable!int[] nullables;
    Nullable!int[] options;
    JSONValue[] untyped_things;
    IntFloatParametrizedRecord parametrized_record;
    KindParametrizedTuple parametrized_tuple;
    uint16_t wrapped;
    AliasOfAliasOfAlias aaa;
}

@trusted Root fromJson(T : Root)(JSONValue x) {
    Root obj;
    obj.id = ("ID" in x) ? _atd_read_string(x["ID"]) : _atd_missing_json_field!(typeof(obj.id))("Root", "ID");
    obj.await = ("await" in x) ? _atd_read_bool(x["await"]) : _atd_missing_json_field!(typeof(obj.await))("Root", "await");
    obj.integer = ("integer" in x) ? _atd_read_int(x["integer"]) : _atd_missing_json_field!(typeof(obj.integer))("Root", "integer");
    obj.x___init__ = ("__init__" in x) ? _atd_read_float(x["__init__"]) : _atd_missing_json_field!(typeof(obj.x___init__))("Root", "__init__");
    obj.float_with_auto_default = ("float_with_auto_default" in x) ? _atd_read_float(x["float_with_auto_default"]) : 0.0;
    obj.float_with_default = ("float_with_default" in x) ? _atd_read_float(x["float_with_default"]) : 0.1;
    obj.items = ("items" in x) ? _atd_read_list!(_atd_read_list!(_atd_read_int))(x["items"]) : _atd_missing_json_field!(typeof(obj.items))("Root", "items");
    obj.maybe = ("maybe" in x) ? _atd_read_option!(_atd_read_int)(x["maybe"]) : typeof(obj.maybe).init;
    obj.extras = ("extras" in x) ? _atd_read_list!(_atd_read_int)(x["extras"]) : [];
    obj.answer = ("answer" in x) ? _atd_read_int(x["answer"]) : 42;
    obj.aliased = ("aliased" in x) ? fromJson!Alias(x["aliased"]) : _atd_missing_json_field!(typeof(obj.aliased))("Root", "aliased");
    obj.point = ("point" in x) ? ((JSONValue x) @trusted { 
    if (x.type != JSONType.array || x.array.length != 2)
      throw _atd_bad_json("Tuple of size 2", x);
    return tuple(_atd_read_float(x[0]), _atd_read_float(x[1]));
  })(x["point"]) : _atd_missing_json_field!(typeof(obj.point))("Root", "point");
    obj.kind = ("kind" in x) ? fromJson!Kind(x["kind"]) : _atd_missing_json_field!(typeof(obj.kind))("Root", "kind");
    obj.kinds = ("kinds" in x) ? _atd_read_list!(fromJson!Kind)(x["kinds"]) : _atd_missing_json_field!(typeof(obj.kinds))("Root", "kinds");
    obj.assoc1 = ("assoc1" in x) ? _atd_read_list!(((JSONValue x) @trusted { 
    if (x.type != JSONType.array || x.array.length != 2)
      throw _atd_bad_json("Tuple of size 2", x);
    return tuple(_atd_read_float(x[0]), _atd_read_int(x[1]));
  }))(x["assoc1"]) : _atd_missing_json_field!(typeof(obj.assoc1))("Root", "assoc1");
    obj.assoc2 = ("assoc2" in x) ? _atd_read_object_to_tuple_list!(_atd_read_int)(x["assoc2"]) : _atd_missing_json_field!(typeof(obj.assoc2))("Root", "assoc2");
    obj.assoc3 = ("assoc3" in x) ? _atd_read_array_to_assoc_dict!(_atd_read_float, _atd_read_int)(x["assoc3"]) : _atd_missing_json_field!(typeof(obj.assoc3))("Root", "assoc3");
    obj.assoc4 = ("assoc4" in x) ? _atd_read_object_to_assoc_array!(_atd_read_int)(x["assoc4"]) : _atd_missing_json_field!(typeof(obj.assoc4))("Root", "assoc4");
    obj.nullables = ("nullables" in x) ? _atd_read_list!(_atd_read_nullable!(_atd_read_int))(x["nullables"]) : _atd_missing_json_field!(typeof(obj.nullables))("Root", "nullables");
    obj.options = ("options" in x) ? _atd_read_list!(_atd_read_option!(_atd_read_int))(x["options"]) : _atd_missing_json_field!(typeof(obj.options))("Root", "options");
    obj.untyped_things = ("untyped_things" in x) ? _atd_read_list!(((JSONValue x) => x))(x["untyped_things"]) : _atd_missing_json_field!(typeof(obj.untyped_things))("Root", "untyped_things");
    obj.parametrized_record = ("parametrized_record" in x) ? fromJson!IntFloatParametrizedRecord(x["parametrized_record"]) : _atd_missing_json_field!(typeof(obj.parametrized_record))("Root", "parametrized_record");
    obj.parametrized_tuple = ("parametrized_tuple" in x) ? fromJson!KindParametrizedTuple(x["parametrized_tuple"]) : _atd_missing_json_field!(typeof(obj.parametrized_tuple))("Root", "parametrized_tuple");
    obj.wrapped = ("wrapped" in x) ? _atd_read_wrap!(fromJson!St, (St e) => ((St st) => st.to!int.to!uint16_t)(e))(x["wrapped"]) : _atd_missing_json_field!(typeof(obj.wrapped))("Root", "wrapped");
    obj.aaa = ("aaa" in x) ? fromJson!AliasOfAliasOfAlias(x["aaa"]) : _atd_missing_json_field!(typeof(obj.aaa))("Root", "aaa");
    return obj;
}
@trusted JSONValue toJson(T : Root)(T obj) {
    JSONValue res;
    res["ID"] = _atd_write_string(obj.id);
    res["await"] = _atd_write_bool(obj.await);
    res["integer"] = _atd_write_int(obj.integer);
    res["__init__"] = _atd_write_float(obj.x___init__);
    res["float_with_auto_default"] = _atd_write_float(obj.float_with_auto_default);
    res["float_with_default"] = _atd_write_float(obj.float_with_default);
    res["items"] = _atd_write_list!(_atd_write_list!(_atd_write_int))(obj.items);
    if (!obj.maybe.isNull)
        res["maybe"] = _atd_write_option!(_atd_write_int)(obj.maybe);
    res["extras"] = _atd_write_list!(_atd_write_int)(obj.extras);
    res["answer"] = _atd_write_int(obj.answer);
    res["aliased"] = ((Alias x) => x.toJson!(Alias))(obj.aliased);
    res["point"] = ((Tuple!(float, float) x) => JSONValue([_atd_write_float(x[0]), _atd_write_float(x[1])]))(obj.point);
    res["kind"] = ((Kind x) => x.toJson!(Kind))(obj.kind);
    res["kinds"] = _atd_write_list!(((Kind x) => x.toJson!(Kind)))(obj.kinds);
    res["assoc1"] = _atd_write_list!(((Tuple!(float, int) x) => JSONValue([_atd_write_float(x[0]), _atd_write_int(x[1])])))(obj.assoc1);
    res["assoc2"] = _atd_write_tuple_list_to_object!(_atd_write_int)(obj.assoc2);
    res["assoc3"] = _atd_write_assoc_dict_to_array!(_atd_write_float, _atd_write_int)(obj.assoc3);
    res["assoc4"] = _atd_write_assoc_array_to_object!(_atd_write_int)(obj.assoc4);
    res["nullables"] = _atd_write_list!(_atd_write_nullable!(_atd_write_int))(obj.nullables);
    res["options"] = _atd_write_list!(_atd_write_option!(_atd_write_int))(obj.options);
    res["untyped_things"] = _atd_write_list!((JSONValue x) => x)(obj.untyped_things);
    res["parametrized_record"] = ((IntFloatParametrizedRecord x) => x.toJson!(IntFloatParametrizedRecord))(obj.parametrized_record);
    res["parametrized_tuple"] = ((KindParametrizedTuple x) => x.toJson!(KindParametrizedTuple))(obj.parametrized_tuple);
    res["wrapped"] = _atd_write_wrap!(((St x) => x.toJson!(St)), (uint16_t e) => ((uint16_t e) => St(e.to!int))(e))(obj.wrapped);
    res["aaa"] = ((AliasOfAliasOfAlias x) => x.toJson!(AliasOfAliasOfAlias))(obj.aaa);
    return res;
}


struct RequireField {
    string req;
}

@trusted RequireField fromJson(T : RequireField)(JSONValue x) {
    RequireField obj;
    obj.req = ("req" in x) ? _atd_read_string(x["req"]) : _atd_missing_json_field!(typeof(obj.req))("RequireField", "req");
    return obj;
}
@trusted JSONValue toJson(T : RequireField)(T obj) {
    JSONValue res;
    res["req"] = _atd_write_string(obj.req);
    return res;
}


struct RecordWithWrappedType {
    int item;
}

@trusted RecordWithWrappedType fromJson(T : RecordWithWrappedType)(JSONValue x) {
    RecordWithWrappedType obj;
    obj.item = ("item" in x) ? _atd_read_wrap!(_atd_read_string, (string e) => to!int(e))(x["item"]) : _atd_missing_json_field!(typeof(obj.item))("RecordWithWrappedType", "item");
    return obj;
}
@trusted JSONValue toJson(T : RecordWithWrappedType)(T obj) {
    JSONValue res;
    res["item"] = _atd_write_wrap!(_atd_write_string, (int e) => to!string(e))(obj.item);
    return res;
}


struct Password{uint32_t _data; alias _data this;
this(uint32_t init) @safe {_data = init;}
this(Password init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : Password)(Password e) {
    return _atd_write_wrap!(_atd_write_int, (uint32_t e) => to!int(e))(e);
}
@trusted Password fromJson(T : Password)(JSONValue e) {
    return Password(_atd_read_wrap!(_atd_read_int, (int e) => to!uint32_t(e))(e));
}


struct Pair{Tuple!(string, int) _data; alias _data this;
this(Tuple!(string, int) init) @safe {_data = init;}
this(Pair init) @safe {_data = init._data;}
this(T...)(T args) @safe {_data = tuple(args);}
}
@trusted JSONValue toJson(T : Pair)(Pair e) {
    return ((Tuple!(string, int) x) => JSONValue([_atd_write_string(x[0]), _atd_write_int(x[1])]))(e);
}
@trusted Pair fromJson(T : Pair)(JSONValue e) {
    return Pair(((JSONValue x) @trusted { 
    if (x.type != JSONType.array || x.array.length != 2)
      throw _atd_bad_json("Tuple of size 2", x);
    return tuple(_atd_read_string(x[0]), _atd_read_int(x[1]));
  })(e));
}


// Original type: frozen = [ ... | A | ... ]
struct A {}
@trusted JSONValue toJson(T : A)(T e) {
    return JSONValue("A");
}


// Original type: frozen = [ ... | B of ... | ... ]
struct B { int value; }
@trusted JSONValue toJson(T : B)(T e) {
    return JSONValue([JSONValue("B"), _atd_write_int(e.value)]);
}


struct Frozen{ SumType!(A, B) _data; alias _data this;
@safe this(T)(T init) {_data = init;} @safe this(Frozen init) {_data = init._data;}}

@trusted Frozen fromJson(T : Frozen)(JSONValue x) {
    if (x.type == JSONType.string) {
        if (x.str == "A") 
            return Frozen(A());
        throw _atd_bad_json("Frozen", x);
    }
    if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) {
        string cons = x[0].str;
        if (cons == "B")
            return Frozen(B(_atd_read_int(x[1])));
        throw _atd_bad_json("Frozen", x);
    }
    throw _atd_bad_json("Frozen", x);
}

@trusted JSONValue toJson(T : Frozen)(T x) {
    return x.match!(
    (A v) => v.toJson!(A),
(B v) => v.toJson!(B)
    );
}


struct DefaultList {
    int[] items = [];
}

@trusted DefaultList fromJson(T : DefaultList)(JSONValue x) {
    DefaultList obj;
    obj.items = ("items" in x) ? _atd_read_list!(_atd_read_int)(x["items"]) : [];
    return obj;
}
@trusted JSONValue toJson(T : DefaultList)(T obj) {
    JSONValue res;
    res["items"] = _atd_write_list!(_atd_write_int)(obj.items);
    return res;
}


struct Credential {
    string name;
    Password password;
}

@trusted Credential fromJson(T : Credential)(JSONValue x) {
    Credential obj;
    obj.name = ("name" in x) ? _atd_read_string(x["name"]) : _atd_missing_json_field!(typeof(obj.name))("Credential", "name");
    obj.password = ("password" in x) ? fromJson!Password(x["password"]) : _atd_missing_json_field!(typeof(obj.password))("Credential", "password");
    return obj;
}
@trusted JSONValue toJson(T : Credential)(T obj) {
    JSONValue res;
    res["name"] = _atd_write_string(obj.name);
    res["password"] = ((Password x) => x.toJson!(Password))(obj.password);
    return res;
}


struct Credentials{Credential[] _data; alias _data this;
this(Credential[] init) @safe {_data = init;}
this(Credentials init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : Credentials)(Credentials e) {
    return _atd_write_list!(((Credential x) => x.toJson!(Credential)))(e);
}
@trusted Credentials fromJson(T : Credentials)(JSONValue e) {
    return Credentials(_atd_read_list!(fromJson!Credential)(e));
}


struct AliasOfAlias{uint16_t _data; alias _data this;
this(uint16_t init) @safe {_data = init;}
this(AliasOfAlias init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : AliasOfAlias)(AliasOfAlias e) {
    return _atd_write_wrap!(((Alias3 x) => x.toJson!(Alias3)), (uint16_t e) => to!uint32_t(e))(e);
}
@trusted AliasOfAlias fromJson(T : AliasOfAlias)(JSONValue e) {
    return AliasOfAlias(_atd_read_wrap!(fromJson!Alias3, (Alias3 e) => to!uint16_t(e))(e));
}


struct Alias2{int[] _data; alias _data this;
this(int[] init) @safe {_data = init;}
this(Alias2 init) @safe {_data = init._data;}

}
@trusted JSONValue toJson(T : Alias2)(Alias2 e) {
    return _atd_write_list!(_atd_write_int)(e);
}
@trusted Alias2 fromJson(T : Alias2)(JSONValue e) {
    return Alias2(_atd_read_list!(_atd_read_int)(e));
}
