Created
October 15, 2012 18:51
-
-
Save beatgammit/3894337 to your computer and use it in GitHub Desktop.
JSON marshal/unmarshal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.conv; | |
import std.json; | |
import std.traits; | |
/* Marshal */ | |
auto marshalJSON(T)(in T val) if (isSomeString!T) { | |
JSONValue ret; | |
ret.str = val; | |
ret.type = JSON_TYPE.STRING; | |
return ret; | |
} | |
unittest { | |
auto a = marshalJSON!string("hello"); | |
assert(a.type == JSON_TYPE.STRING); | |
assert(a.str == "hello"); | |
} | |
auto marshalJSON(T)(in T val) if (isIntegral!T && isSigned!T) { | |
JSONValue ret; | |
ret.integer = val; | |
ret.type = JSON_TYPE.INTEGER; | |
return ret; | |
} | |
unittest { | |
auto a = marshalJSON!int(5); | |
assert(a.type == JSON_TYPE.INTEGER); | |
assert(a.integer == 5); | |
auto b = marshalJSON!int(-5); | |
assert(b.type == JSON_TYPE.INTEGER); | |
assert(b.integer == -5); | |
} | |
// handles unsigned integer types | |
auto marshalJSON(T)(in T val) if (isIntegral!T && isUnsigned!T) { | |
JSONValue ret; | |
ret.uinteger = val; | |
ret.type = JSON_TYPE.UINTEGER; | |
return ret; | |
} | |
unittest { | |
uint t = 5; | |
auto a = marshalJSON!uint(t); | |
assert(a.type == JSON_TYPE.UINTEGER); | |
assert(a.uinteger == 5); | |
} | |
// handles floating point numbers | |
auto marshalJSON(T)(in T val) if (isFloatingPoint!T) { | |
JSONValue ret; | |
ret.floating = val; | |
ret.type = JSON_TYPE.FLOAT; | |
return ret; | |
} | |
unittest { | |
auto a = marshalJSON!float(5); | |
assert(a.type == JSON_TYPE.FLOAT); | |
assert(a.floating == 5f); | |
} | |
// handles booleans | |
auto marshalJSON(T)(in T val) if (isBoolean!T) { | |
JSONValue ret; | |
ret.type = val ? JSON_TYPE.TRUE : JSON_TYPE.FALSE; | |
return ret; | |
} | |
unittest { | |
auto a = marshalJSON!bool(true); | |
assert(a.type == JSON_TYPE.TRUE); | |
auto b = marshalJSON!bool(false); | |
assert(b.type == JSON_TYPE.FALSE); | |
} | |
auto marshalJSON(T)(in T val) if (isPointer!T) { | |
return marshalJSON!(PointerTarget!T)(*val); | |
} | |
// JSON types to pointers | |
unittest { | |
int* a = new int; | |
*a = 5; | |
auto j = marshalJSON!(int*)(a); | |
assert(j.type == JSON_TYPE.INTEGER); | |
assert(j.integer == *a); | |
} | |
// handles arrays, both static and dynamic | |
auto marshalJSON(T)(in T val) if (!isSomeString!T && isArray!T) { | |
JSONValue ret; | |
ret.type = JSON_TYPE.ARRAY; | |
ret.array.length = val.length; | |
foreach (i, elem; val) { | |
ret.array[i] = marshalJSON!(ForeachType!T)(elem); | |
} | |
return ret; | |
} | |
// JSON array: static array, dynamic array | |
unittest { | |
auto arr = [2, 5]; | |
auto a = marshalJSON!(int[])(arr); | |
assert(a.type == JSON_TYPE.ARRAY); | |
foreach (i, _; a.array) { | |
assert(a.array[i].type == JSON_TYPE.INTEGER); | |
assert(a.array[i].integer == arr[i]); | |
} | |
} | |
// handles JSON objects to associative arrays | |
auto marshalJSON(T)(in T val) if (isAssociativeArray!T && is(KeyType!T : string)) { | |
JSONValue ret; | |
ret.type = JSON_TYPE.OBJECT; | |
foreach (k, v; val) { | |
ret.object[k] = marshalJSON!(ValueType!(T))(v); | |
} | |
return ret; | |
} | |
// JSON object: associative array where key must be a string | |
unittest { | |
auto aMap = ["one": 1]; | |
auto a = marshalJSON!(int[string])(aMap); | |
assert(a.type == JSON_TYPE.OBJECT); | |
foreach (k, v; a.object) { | |
assert(v.type == JSON_TYPE.INTEGER); | |
assert(aMap[k] == v.integer); | |
} | |
auto bMap = ["one": 1f]; | |
auto b = marshalJSON!(float[string])(bMap); | |
assert(b.type == JSON_TYPE.OBJECT); | |
foreach (k, v; b.object) { | |
assert(v.type == JSON_TYPE.FLOAT); | |
assert(bMap[k] == v.floating); | |
} | |
} | |
// handles JSON objects to classes/structs | |
auto marshalJSON(T)(in T val) if (is(T == class) || is(T == struct)) { | |
JSONValue ret; | |
ret.type = JSON_TYPE.OBJECT; | |
foreach (t; __traits(allMembers, T)) { | |
static if ( | |
// static function | |
is(typeof(mixin("T." ~ t)) == function) || | |
is(typeof(mixin("T." ~ t)) == delegate) || | |
// member function | |
is(typeof(mixin("val." ~ t)) == function) || | |
is(typeof(mixin("val." ~ t)) == delegate) || | |
// extra class stuff | |
t == "Monitor" | |
) { | |
continue; | |
} else { | |
ret.object[t] = marshalJSON!(typeof(mixin("T." ~ t)))(mixin("val." ~ t)); | |
} | |
} | |
return ret; | |
} | |
// JSON object: class | |
unittest { | |
import std.array; | |
import std.format; | |
static class A { | |
int z; | |
float y; | |
uint x; | |
uint[3] w; | |
string toString() { | |
auto writer = appender!string(); | |
formattedWrite(writer, "%d %f %d %s", z, y, x, w); | |
return writer.data; | |
} | |
} | |
A a = new A; | |
a.z = 3; | |
a.y = 3.3f; | |
a.x = 7; | |
a.w = [8, 7, 2]; | |
auto j = marshalJSON!A(a); | |
assert(j.type == JSON_TYPE.OBJECT); | |
assert(j.object["z"].type == JSON_TYPE.INTEGER); | |
assert(j.object["z"].integer == a.z); | |
assert(j.object["y"].type == JSON_TYPE.FLOAT); | |
assert(j.object["y"].floating == a.y); | |
assert(j.object["x"].type == JSON_TYPE.UINTEGER); | |
assert(j.object["x"].uinteger == a.x); | |
assert(j.object["w"].type == JSON_TYPE.ARRAY); | |
foreach (i, v; j.object["w"].array) { | |
assert(v.type == JSON_TYPE.UINTEGER); | |
assert(a.w[i] == v.uinteger); | |
} | |
} | |
// JSON objects: structs | |
unittest { | |
import std.array; | |
import std.format; | |
static struct A { | |
int a; | |
double b; | |
string c; | |
int[] d; | |
int[string] e; | |
//double* f; | |
string toString() { | |
auto writer = appender!string(); | |
formattedWrite(writer, "%d %f '%s' %s %s", a, b, c, d, e); | |
return writer.data; | |
} | |
} | |
A a = {a: 5, b: 6., c: "hello", d: [1, 2, 3]}; | |
a.e = ["one": 1, "two": 2]; | |
auto j = marshalJSON!A(a); | |
assert(j.type == JSON_TYPE.OBJECT); | |
assert(j.object["a"].type == JSON_TYPE.INTEGER); | |
assert(j.object["a"].integer == a.a); | |
assert(j.object["b"].type == JSON_TYPE.FLOAT); | |
assert(j.object["b"].floating == a.b); | |
assert(j.object["c"].type == JSON_TYPE.STRING); | |
assert(j.object["c"].str == a.c); | |
assert(j.object["d"].type == JSON_TYPE.ARRAY); | |
foreach (i, v; j.object["d"].array) { | |
assert(v.type == JSON_TYPE.INTEGER); | |
assert(v.integer == a.d[i]); | |
} | |
assert(j.object["e"].type == JSON_TYPE.OBJECT); | |
foreach (k, v; j.object["e"].object) { | |
assert(v.type == JSON_TYPE.INTEGER); | |
assert(v.integer == a.e[k]); | |
} | |
} | |
// embedded struct in class | |
unittest { | |
struct A { | |
int c; | |
} | |
static class B { | |
A a; | |
A* b; | |
} | |
auto b = new B; | |
b.a.c = 1; | |
b.b = new A; | |
b.b.c = 2; | |
auto j = marshalJSON!B(b); | |
assert(j.type == JSON_TYPE.OBJECT); | |
assert(j["a"].type == JSON_TYPE.OBJECT); | |
assert(j["a"].object["c"].type == JSON_TYPE.INTEGER); | |
assert(j["a"].object["c"].integer == b.a.c); | |
assert(j["b"].type == JSON_TYPE.OBJECT); | |
assert(j["b"].object["c"].type == JSON_TYPE.INTEGER); | |
assert(j["b"].object["c"].integer == b.b.c); | |
} | |
// Subclasses | |
unittest { | |
static class A { | |
int a; | |
} | |
static class B : A { | |
int b; | |
} | |
auto b = new B; | |
b.a = 1; | |
b.b = 2; | |
auto j = marshalJSON!B(b); | |
assert(j.type == JSON_TYPE.OBJECT); | |
assert(j.object["a"].type == JSON_TYPE.INTEGER); | |
assert(j.object["a"].integer == b.a); | |
assert(j.object["b"].type == JSON_TYPE.INTEGER); | |
assert(j.object["b"].integer == b.b); | |
} | |
/* Unmarshal */ | |
// handles strings | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (isSomeString!T) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.STRING); | |
} body { | |
ret = to!T(val.str); | |
} | |
// JSON type: int, float, string etc. | |
unittest { | |
string s; | |
unmarshalJSON!string(`"5"`, s); | |
assert(s == "5"); | |
} | |
// handles signed integers | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (isIntegral!T && isSigned!T) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.INTEGER); | |
} body { | |
ret = to!T(val.integer); | |
} | |
unittest { | |
int a; | |
unmarshalJSON!int(`5`, a); | |
assert(a == 5); | |
int b; | |
unmarshalJSON!int(`-5`, b); | |
assert(b == -5); | |
} | |
// handles unsigned integer types | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (isIntegral!T && isUnsigned!T) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.UINTEGER || val.type == JSON_TYPE.INTEGER); | |
} body { | |
// we know it's unsigned, and uinteger & integer are in the same union, | |
// so both uinteger & integer will be equivalent | |
ret = to!T(val.uinteger); | |
} | |
unittest { | |
uint a; | |
unmarshalJSON!uint(`5`, a); | |
assert(a == 5); | |
} | |
// handles floating point numbers. also casts integers to floats | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (isFloatingPoint!T) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.FLOAT || val.type == JSON_TYPE.INTEGER || val.type == JSON_TYPE.UINTEGER); | |
} body { | |
switch (val.type) { | |
case JSON_TYPE.FLOAT: | |
ret = to!T(val.floating); | |
break; | |
case JSON_TYPE.INTEGER: | |
ret = to!T(val.integer); | |
break; | |
case JSON_TYPE.UINTEGER: | |
ret = to!T(val.uinteger); | |
break; | |
default: | |
// TODO: don't assert | |
assert(0); | |
return false; | |
} | |
return true; | |
} | |
unittest { | |
float a; | |
unmarshalJSON!float(`5.0`, a); | |
assert(a == 5.0f); | |
} | |
// handles booleans | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (isBoolean!T) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.TRUE || val.type == JSON_TYPE.FALSE); | |
} body { | |
ret = val.type == JSON_TYPE.TRUE; | |
} | |
unittest { | |
bool a; | |
unmarshalJSON!bool(`true`, a); | |
assert(a); | |
bool b; | |
unmarshalJSON!bool(`false`, b); | |
assert(!b); | |
} | |
auto unmarshalJSON(T)(JSONValue val, T ret) if (isPointer!T) | |
in { | |
assert(ret != null); | |
} body { | |
PointerTarget!T ret2; | |
unmarshalJSON!(PointerTarget!T)(val, ret2); | |
*ret = ret2; | |
} | |
// JSON types to pointers | |
unittest { | |
int* b = new int; | |
*b = 3; | |
unmarshalJSON!(int*)(`5`, b); | |
assert(*b == 5); | |
} | |
// handles arrays, both static and dynamic | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (!isSomeString!T && (isArray!T || isStaticArray!T)) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.ARRAY); | |
static if (isStaticArray!(T)) { | |
// TODO: don't assert | |
assert(val.array.length <= T.length); | |
} | |
} body { | |
T ret2; | |
static if (isDynamicArray!T) { | |
ret2.length = val.array.length; | |
} | |
foreach (i, elem; val.array) { | |
unmarshalJSON!(ForeachType!T)(elem, ret2[i]); | |
} | |
ret = ret2; | |
} | |
// JSON array: static array, dynamic array | |
unittest { | |
import core.exception; | |
import std.exception; | |
int[] arr; | |
unmarshalJSON!(int[])(`[2, 5]`, arr); | |
assert(arr == [2, 5]); | |
float[] arr2; | |
unmarshalJSON!(float[])(`[2.0, 5.0]`, arr2); | |
assert(arr2 == [2.0f, 5.0f]); | |
int[2] arr3; | |
unmarshalJSON!(int[2])(`[1, 2]`, arr3); | |
assert(arr3 == [1, 2]); | |
int[2] arr4; | |
unmarshalJSON!(int[2])(`[1]`, arr4); | |
assert(arr4 == [1, 0]); | |
int[2] arr5; | |
assertThrown!AssertError(unmarshalJSON!(int[2])(`[1, 2, 3]`, arr5), "Cannot unmarshal into static array smaller than the data"); | |
} | |
// handles JSON objects to associative arrays | |
auto unmarshalJSON(T)(JSONValue val, out T ret) if (isAssociativeArray!T && is(KeyType!T : string)) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.OBJECT); | |
} body { | |
T ret2; | |
foreach (k, v; val.object) { | |
unmarshalJSON!(ValueType!(T))(v, ret2[k]); | |
} | |
ret = ret2; | |
} | |
// JSON object: associative array where key must be a string | |
unittest { | |
int[string] a; | |
unmarshalJSON!(int[string])(`{"one": 1}`, a); | |
assert(a == ["one": 1]); | |
float[string] b; | |
unmarshalJSON!(float[string])(`{"one": 1}`, b); | |
assert(b == ["one": 1f]); | |
} | |
// handles JSON objects to classes/structs | |
auto unmarshalJSON(T)(JSONValue val, ref T ret) if (is(T == class) || is(T == struct)) | |
in { | |
// TODO: don't assert | |
assert(val.type == JSON_TYPE.OBJECT); | |
static if (is(T == class)) { | |
assert(ret); | |
} | |
} body { | |
foreach (k, v; val.object) { | |
foreach (t; __traits(allMembers, T)) { | |
static if ( | |
// static function | |
is(typeof(mixin("T." ~ t)) == function) || | |
is(typeof(mixin("T." ~ t)) == delegate) || | |
// member function | |
is(typeof(mixin("ret." ~ t)) == function) || | |
is(typeof(mixin("ret." ~ t)) == delegate) || | |
// extra class stuff | |
t == "Monitor" | |
) { | |
continue; | |
} else { | |
if (k == t) { | |
unmarshalJSON!(typeof(mixin("ret." ~ t)))(v, mixin("ret." ~ t)); | |
} | |
} | |
} | |
} | |
} | |
// JSON object: class | |
unittest { | |
import std.array; | |
import std.format; | |
static class A { | |
int z; | |
float y; | |
uint x; | |
uint[3] w; | |
string toString() { | |
auto writer = appender!string(); | |
formattedWrite(writer, "%d %f %d %s", z, y, x, w); | |
return writer.data; | |
} | |
} | |
auto a = new A; | |
unmarshalJSON!A(`{"z": 3, "y": 3.3, "x": 7, "w": [8, 7, 2]}`, a); | |
assert(a.z == 3); | |
assert(a.y == 3.3f); | |
assert(a.x == 7); | |
assert(a.w == [8, 7, 2]); | |
} | |
// JSON objects: structs | |
unittest { | |
import std.array; | |
import std.format; | |
static struct A { | |
int a; | |
double b; | |
string c; | |
int[] d; | |
int[string] e; | |
double* f; | |
string toString() { | |
auto writer = appender!string(); | |
formattedWrite(writer, "%d %f '%s' %s %s", a, b, c, d, e); | |
return writer.data; | |
} | |
} | |
A a; | |
a.f = new double; | |
unmarshalJSON!A(`{"a": 5, "b": 6.0, "c": "hello", "d": [1, 2, 3], "e": {"one": 1, "two": 2}, "f": 7}`, a); | |
assert(a.a == 5); | |
assert(a.b == 6.0f); | |
assert(a.c == "hello"); | |
assert(a.d == [1, 2, 3]); | |
assert(a.e == ["one": 1, "two": 2]); | |
assert(*a.f == 7f); | |
} | |
// embedded struct in class | |
unittest { | |
struct A { | |
int c; | |
} | |
static class B { | |
A a; | |
A* b; | |
this() { | |
this.b = new A; | |
} | |
} | |
auto b = new B; | |
unmarshalJSON!B(`{"a": {"c": 3}, "b": {"c": 4}}`, b); | |
assert(b.a.c == 3); | |
assert(b.b.c == 4); | |
} | |
// Subclasses | |
unittest { | |
static class A { | |
int a; | |
} | |
static class B : A { | |
int b; | |
} | |
auto b = new B; | |
unmarshalJSON!B(`{"a": 5, "b": 3}`, b); | |
assert(b.a == 5); | |
assert(b.b == 3); | |
} | |
auto unmarshalJSON(T)(string s, ref T ret) { | |
unmarshalJSON!T(parseJSON(s), ret); | |
} | |
void main() { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment