1 module painlessjson;
2 
3 import std.conv;
4 import std.json;
5 import std.range;
6 import std.traits;
7 
8 version(unittest)
9 {
10     import std.algorithm;
11     import std.stdio;
12 
13     struct Point
14     {
15         double x = 0;
16         double y = 1;
17         this(double x_, double y_)
18         {
19             x = x_;
20             y = y_;
21         }
22 
23         string foo()
24         {
25             writeln("Functions should not be called");
26             return "Noooooo!";
27         }
28 
29         static string bar()
30         {
31             writeln("Static functions should not be called");
32             return "Noooooo!";
33         }
34 
35     }
36 
37     class PointC
38     {
39         double x = 0;
40         double y = 1;
41         this()
42         {
43         }
44 
45         ;
46         this(double x_, double y_)
47         {
48             x = x_;
49             y = y_;
50         }
51 
52         string foo()
53         {
54             writeln("Class functions should not be called");
55             return "Noooooo!";
56         }
57 
58     }
59 
60     class PointPrivate
61     {
62         private double _x;
63         private double _y;
64         this(double x_, double y_)
65         {
66             _x = x_;
67             _y = y_;
68         }
69 
70         string foo()
71         {
72             writeln("Class functions should not be called");
73             return "Noooooo!";
74         }
75 
76         double x()
77         {
78             return _x;
79         }
80 
81         double y()
82         {
83             return _y;
84         }
85 
86         static PointPrivate _fromJSON(JSONValue value)
87         {
88             return new PointPrivate(fromJSON!double(value["x"]), fromJSON!double(value["y"]));
89         }
90 
91         JSONValue _toJSON()
92         {
93             JSONValue[string] json;
94             json["x"] = JSONValue(x);
95             json["y"] = JSONValue(y);
96             return JSONValue(json);
97         }
98 
99     }
100 
101 }
102 
103 /// Template function that converts any object to JSON
104 JSONValue toJSON(T)(T object)
105 {
106     static if (__traits(compiles, (T t)
107     {
108         JSONValue(t);
109     }
110     ))
111     {
112         return JSONValue(object);
113     }
114     else static if (isArray!T)
115     {
116          // Range
117         JSONValue[] jsonRange;
118         jsonRange = map!((el) => el.toJSON)(object).array;
119         return JSONValue(jsonRange);
120     }
121     else static if (isAssociativeArray!T)
122     {
123          // Range
124         JSONValue[string] jsonAA;
125         foreach (key, value; object)
126         {
127             jsonAA[key.toJSON.toString] = value.toJSON;
128         }
129         return JSONValue(jsonAA);
130     }
131     else static if (__traits(compiles, (T t)
132     {
133         return object._toJSON();
134     }
135     ))
136     {
137         return object._toJSON();
138     }
139     else
140     {
141         JSONValue[string] json;
142         // Getting all member variables (there is probably an easier way)
143         foreach (name; __traits(allMembers, T))
144         {
145             static if (__traits(compiles, __traits(getMember, object, name)
146                 .toJSON)
147                 //&& __traits(compiles, __traits(getMember, object, name)) 
148                 //  Skip Functions 
149                  && !isSomeFunction!(__traits(getMember, object, name)))
150             {
151                  // Can we get a value? (filters out void * this)
152                 json[name] = __traits(getMember, object, name).toJSON;
153             }
154         }
155         return JSONValue(json);
156     }
157 }
158 
159 
160 /// Converting common types
161 unittest
162 {
163     assert(5.toJSON!int == JSONValue(5));
164     assert(4.toJSON != JSONValue(5));
165     assert(5.4.toJSON == JSONValue(5.4));
166     assert(toJSON("test") == JSONValue("test"));
167     assert(toJSON(JSONValue("test")) == JSONValue("test"));
168 }
169 
170 
171 /// Converting InputRanges
172 unittest
173 {
174     assert([1, 2].toJSON.toString == "[1,2]");
175 }
176 
177 
178 /// User structs
179 unittest
180 {
181     Point p;
182     assert(toJSON(p).toString == q{{"x":0,"y":1}});
183 }
184 
185 
186 /// Array of structs
187 unittest
188 {
189     Point[] ps = [Point(-1, 1), Point(2, 3)];
190     assert(toJSON(ps).toString == q{[{"x":-1,"y":1},{"x":2,"y":3}]});
191 }
192 
193 
194 /// User class
195 unittest
196 {
197     PointC p = new PointC(1, -2);
198     assert(toJSON(p).toString == q{{"x":1,"y":-2}});
199 }
200 
201 
202 /// User class with private fields
203 unittest
204 {
205     PointPrivate p = new PointPrivate(-1, 2);
206     assert(toJSON(p).toString == q{{"x":-1,"y":2}});
207 }
208 
209 
210 /// Array of classes
211 unittest
212 {
213     PointC[] ps = [new PointC(-1, 1), new PointC(2, 3)];
214     assert(toJSON(ps).toString == q{[{"x":-1,"y":1},{"x":2,"y":3}]});
215 }
216 
217 
218 /// Associative array
219 unittest
220 {
221     string[int] aa = [0 : "a", 1 : "b"];
222     // In JSON (D) only string based associative arrays are supported, so:
223     assert(aa.toJSON.toString == q{{"0":"a","1":"b"}});
224     Point[int] aaStruct = [0 : Point(-1, 1), 1 : Point(2, 0)];
225     assert(aaStruct.toJSON.toString == q{{"0":{"x":-1,"y":1},"1":{"x":2,"y":0}}});
226 }
227 
228 
229 /// Overloaded toJSON
230 unittest
231 {
232     class A
233     {
234         double x = 0;
235         double y = 1;
236         JSONValue toJSON()
237         {
238             JSONValue[string] json;
239             json["x"] = x;
240             return JSONValue(json);
241         }
242 
243     }
244 
245     auto a = new A;
246     assert(a.toJSON.toString == q{{"x":0}});
247     
248     class B
249     {
250         double x = 0;
251         double y = 1;
252     }
253 
254     
255     // Both templates will now work for B, so this is ambiguous in D. 
256     // Under dmd it looks like the toJSON!T that is loaded first is the one used
257     JSONValue toJSON(T : B)(T b)
258     {
259         JSONValue[string] json;
260         json["x"] = b.x;
261         return JSONValue(json);
262     }
263 
264     auto b = new B;
265     assert(b.toJSON.toString == q{{"x":0,"y":1}});
266     
267     class Z
268     {
269         double x = 0;
270         double y = 1;
271         // Adding an extra value
272         JSONValue toJSON()
273         {
274             JSONValue[string] json = painlessjson.toJSON!Z(this).object;
275             json["add"] = "bla".toJSON;
276             return JSONValue(json);
277         }
278 
279     }
280 
281     auto z = new Z;
282     assert(z.toJSON.toString == q{{"x":0,"y":1,"add":"bla"}});
283 }
284 
285 
286 /// Convert from JSONValue to any other type
287 T fromJSON(T)(JSONValue json)
288 {
289     static if (is(T == JSONValue))
290     {
291         return json;
292     }
293     else static if (isIntegral!T)
294     {
295         return to!T(json.integer);
296     }
297     else static if (isFloatingPoint!T)
298     {
299         if (json.type == JSON_TYPE.INTEGER)
300             return to!T(json.integer);
301         else return to!T(json.floating);
302     }
303     else static if (is(T == string))
304     {
305         return to!T(json.str);
306     }
307     else static if (isBoolean!T)
308     {
309         if (json.type == JSON_TYPE.TRUE)
310             return true;
311         else return false;
312     }
313     else static if (__traits(compiles, 
314     {
315         return T._fromJSON(json);
316     }
317     ))
318     {
319         return T._fromJSON(json);
320     }
321     else
322     {
323         T t;
324         static if (__traits(compiles, cast(Object)(t)) && __traits(compiles,
325             new T))
326         {
327             t = new T;
328         }
329         static if (isArray!T)
330         {
331             t = map!((js) => fromJSON!(typeof(t.front))(js))(json.array).array;
332         }
333         else static if (isAssociativeArray!T)
334         {
335             JSONValue[string] jsonAA = json.object;
336             foreach (k, v; jsonAA)
337             {
338                 t[fromJSON!(typeof(t.keys.front))(parseJSON(k))] = fromJSON!(typeof(t
339                     .values.front))(v);
340             }
341         }
342         else
343         {
344             mixin ("JSONValue[string] jsonAA = json.object;");
345             foreach (name; __traits(allMembers, T))
346             {
347                 static if (__traits(compiles, __traits(getMember, t, name))
348                     && __traits(compiles, typeof(__traits(getMember, t, name)))
349                     //  Skip Functions
350                      && !isSomeFunction!(__traits(getMember, t, name)))
351                 {
352                      // Can we get a value? (filters out void * this)
353                     mixin ("if ( \"" ~ name ~ "\" in jsonAA) t." ~ name
354                         ~ "= fromJSON!(" ~ (typeof(__traits(getMember, t, name)))
355                         .stringof ~ ")(jsonAA[\"" ~ name ~ "\"]);");
356                 }
357             }
358         }
359         return t;
360     }
361 }
362 
363 
364 /// Converting common types
365 unittest
366 {
367     assert(fromJSON!int(JSONValue(1)) == 1);
368     assert(fromJSON!double(JSONValue(1.0)) == 1);
369     assert(fromJSON!double(JSONValue(1.3)) == 1.3);
370     assert(fromJSON!string(JSONValue("str")) == "str");
371     assert(fromJSON!bool(JSONValue(true)) == true);
372     assert(fromJSON!bool(JSONValue(false)) == false);
373     assert(fromJSON!JSONValue(JSONValue(true)) == JSONValue(true));
374 }
375 
376 
377 /// Converting arrays
378 unittest
379 {
380     assert(equal(fromJSON!(int[])(toJSON([1, 2])), [1, 2]));
381 }
382 
383 
384 /// Associative arrays
385 unittest
386 {
387     string[int] aa = [0 : "a", 1 : "b"];
388     auto aaCpy = fromJSON!(string[int])(toJSON(aa));
389     foreach (k, v; aa)
390     {
391         assert(aaCpy[k] == v);
392     }
393 }
394 
395 
396 /// Structs from JSON
397 unittest
398 {
399     auto p = fromJSON!Point(parseJSON(q{{"x":-1,"y":2}}));
400     assert(p.x == -1);
401     assert(p.y == 2);
402     p = fromJSON!Point(parseJSON(q{{"x":2}}));
403     assert(p.x == 2);
404     assert(p.y == 1);
405     p = fromJSON!Point(parseJSON(q{{"y":3}}));
406     assert(p.x == 0);
407     assert(p.y == 3);
408     p = fromJSON!Point(parseJSON(q{{"x":-1,"y":2,"z":3}}));
409     assert(p.x == -1);
410     assert(p.y == 2);
411 }
412 
413 
414 /// Class from JSON
415 unittest
416 {
417     auto p = fromJSON!PointC(parseJSON(q{{"x":-1,"y":2}}));
418     assert(p.x == -1);
419     assert(p.y == 2);
420 }
421 
422 
423 /**
424   Convert class from JSON using "_fromJSON"
425   */
426 unittest
427 {
428     auto p = fromJSON!PointPrivate(parseJSON(q{{"x":-1,"y":2}}));
429     assert(p.x == -1);
430     assert(p.y == 2);
431 }