1 module jsonserialized.deserialization;
2 
3 import std.conv;
4 import stdx.data.json;
5 import std.traits;
6 
7 /// Deserialize the contents of a JSONValue into the specified array.
8 pure void deserializeFromJSONValue(T)(ref T array, in JSONValue jsonValue) if (isArray!T) {
9     alias ElementType = ForeachType!T;
10 
11     // Iterate each item in the array JSONValue and add them to values, converting them to the actual type
12     foreach(jvItem; jsonValue.get!(JSONValue[])) {
13         static if (is(ElementType == struct)) {
14             // This item is a struct - instantiate it
15             ElementType newStruct;
16 
17             // ...deserialize into the new instance
18             newStruct.deserializeFromJSONValue(jvItem);
19 
20             // ...and add it to the array
21             array ~= newStruct;
22         }
23         else static if (is(ElementType == class)) {
24             // The item type is class - create a new instance
25             auto newClass = new ElementType();
26 
27             // ...deserialize into the new instance
28             newClass.deserializeFromJSONValue(jvItem);
29 
30             // ...and add it to the array
31             array ~= newClass;
32         }
33         else static if (isSomeString!ElementType) {
34             array ~= jvItem.get!string.to!ElementType;
35         }
36         else static if (isArray!ElementType) {
37             // An array of arrays. Recursion time!
38             ElementType subArray;
39 
40             subArray.deserializeFromJSONValue(jvItem);
41             array ~= subArray;
42         }
43         else {
44             array ~= jvItem.to!ElementType;
45         }
46     }
47 }
48 
49 /// Deserialize the contents of a JSONValue into the specified associative array.
50 pure void deserializeFromJSONValue(T)(ref T associativeArray, in JSONValue jsonValue) if (isAssociativeArray!T) {
51     alias VType = ValueType!T;
52 
53     // Iterate each item in the JSON object
54     foreach(stringKey, value; jsonValue.get!(JSONValue[string])) {
55         auto key = stringKey.to!(KeyType!T);
56 
57         static if (isAssociativeArray!VType) {
58             /* The associative array's value type is another associative array type.
59                It's recursion time. */
60 
61             if (key in associativeArray) {
62                 associativeArray[key].deserializeFromJSONValue(value);
63             }
64             else {
65                 VType subAssocArray;
66 
67                 subAssocArray.deserializeFromJSONValue(value);
68                 associativeArray[key] = subAssocArray;
69             }
70         }
71         else static if (is(VType == struct)) {
72             // The value type is a struct - instantiate it
73             VType newStruct;
74 
75             // ...deserialize into the new instance
76             newStruct.deserializeFromJSONValue(value);
77 
78             // ...and add it to the associative array
79             associativeArray[key] = newStruct;
80         }
81         else static if (is(VType == class)) {
82             // The value type is class - create a new instance
83             auto newClass = new VType();
84 
85             // ...deserialize into the new instance
86             newClass.deserializeFromJSONValue(value);
87 
88             // ...and add it to the associative array
89             associativeArray[key] = newClass;
90         }
91         else static if (isSomeString!VType) {
92             string v;
93 
94             if (value.hasType!string)
95                 v = value.get!string;
96             else if (value.hasType!long)
97                 v = value.get!long.to!string;
98 
99             associativeArray[key] = v.to!VType;
100         }
101         else {
102             associativeArray[key] = value.to!VType;
103         }
104     }
105 }
106 
107 /// Deserialize the contents of a JSONValue into the specified struct or class.
108 pure void deserializeFromJSONValue(T)(ref T obj, in JSONValue jsonValue) if (is(T == struct) || is(T == class)) {
109     enum fieldNames = FieldNameTuple!T;
110 
111     foreach(fieldName; fieldNames) {
112         alias FieldType = typeof(__traits(getMember, obj, fieldName));
113 
114         if (fieldName !in jsonValue) {
115             continue;
116         }
117 
118         static if (is(FieldType == struct)) {
119             // This field is a struct - recurse into it
120             __traits(getMember, obj, fieldName).deserializeFromJSONValue(jsonValue[fieldName]);
121         }
122         else static if (is(FieldType == class)) {
123             // This field is a class - recurse into it unless it is null
124             if (__traits(getMember, obj, fieldName) !is null) {
125                 __traits(getMember, obj, fieldName).deserializeFromJSONValue(jsonValue[fieldName]);
126             }
127         }
128         else static if (isSomeString!FieldType) {
129             // If the JSONValue does not contain a string, don't try to deserialize
130             if (!jsonValue[fieldName].hasType!string)
131                 continue;
132 
133             // Because all string types are stored as string in JSONValue, get it as string and convert it to the correct string type
134             __traits(getMember, obj, fieldName) = jsonValue[fieldName].get!string.to!FieldType;
135         }
136         else static if (isArray!FieldType) {
137             // If the JSONValue does not contain an array, don't try to deserialize
138             if (!jsonValue[fieldName].hasType!(JSONValue[]))
139                 continue;
140 
141             // Field is an array
142             __traits(getMember, obj, fieldName).deserializeFromJSONValue(jsonValue[fieldName]);
143         }
144         else static if (isAssociativeArray!FieldType) {
145             // Field is an associative array
146             __traits(getMember, obj, fieldName).deserializeFromJSONValue(jsonValue[fieldName]);
147         }
148         else static if (isIntegral!FieldType) {
149             // If the JSONValue type does not contain a long, don't try to deserialize
150             if (!jsonValue[fieldName].hasType!long)
151                 continue;
152 
153             __traits(getMember, obj, fieldName) = jsonValue[fieldName].to!FieldType;
154         }
155         else {
156             __traits(getMember, obj, fieldName) = jsonValue[fieldName].to!FieldType;
157         }
158     }
159 }
160 
161 /// Deserialize the contents of a JSONValue into a struct of type T, returning the newly created struct.
162 pure T deserializeFromJSONValue(T)(in JSONValue jsonValue) if (is(T == struct)) {
163     T obj;
164 
165     obj.deserializeFromJSONValue(jsonValue);
166     return obj;
167 }