Skip to content

Instantly share code, notes, and snippets.

@beatgammit
Created October 15, 2012 18:51
Show Gist options
  • Save beatgammit/3894337 to your computer and use it in GitHub Desktop.
Save beatgammit/3894337 to your computer and use it in GitHub Desktop.
JSON marshal/unmarshal
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