11module mcl.utils.json ;
22import mcl.utils.test;
33import mcl.utils.string ;
4- import std.traits : isNumeric, isArray, isSomeChar, ForeachType, isBoolean, isAssociativeArray;
5- import std.json : JSONValue, JSONOptions, JSONType;
6- import std.conv : to;
7- import std.string : strip;
8- import std.range : front;
9- import std.stdio : writeln;
10- import std.algorithm : map;
11- import std.array : join, array, replace, split;
12- import std.datetime : SysTime;
13- import std.sumtype : SumType, isSumType;
14- import core.stdc.string : strlen;
4+ import std.traits : isNumeric, isArray, isSomeChar, ForeachType, isBoolean, isAssociativeArray;
5+ import std.json : JSONValue, JSONOptions, JSONType;
6+ import std.conv : to;
7+ import std.string : strip;
8+ import std.range : front;
9+ import std.stdio : writeln;
10+ import std.algorithm : map;
11+ import std.array : join, array, replace, split;
12+ import std.datetime : SysTime;
13+ import std.sumtype : SumType, isSumType;
14+ import core.stdc.string : strlen;
15+
16+ string jsonValueToString (in JSONValue value)
17+ {
18+ return value.toString(JSONOptions.doNotEscapeSlashes).strip(" \" " );
19+ }
1520
1621bool tryDeserializeJson (T)(in JSONValue value, out T result)
1722{
18- try {
23+ try
24+ {
1925 result = value.fromJSON! T;
2026 return true ;
21- } catch (Exception e) {
27+ }
28+ catch (Exception e)
29+ {
2230 return false ;
2331 }
2432}
2533
26- T fromJSON (T)(in JSONValue value) {
27- if (value.isNull) {
28- return T.init;
29- }
30- static if (is (T == JSONValue)) {
31- return value;
32- }
33- else static if (is (T == bool ) || is (T == string ) || isSomeChar! T || isNumeric! T || is (T == enum )) {
34- return value.toString(JSONOptions.doNotEscapeSlashes).strip(" \" " ).to! T;
35- }
36- else static if (isSumType! T) {
37- static foreach (SumTypeVariant; T.Types)
38- {{
39- SumTypeVariant result;
40- if (tryDeserializeJson! SumTypeVariant(value, result)) {
41- return T (result);
42- }
43- }}
34+ T fromJSON (T)(in JSONValue value)
35+ {
36+ T result;
37+ if (value.isNull)
38+ result = T.init;
4439
45- throw new Exception (" Failed to deserialize JSON value" );
46- }
47- else static if (isArray! T) {
48- static if ( isBoolean! (ForeachType! T)) {
49- if (value.type == JSONType.string && isBoolean! (ForeachType! T)) {
50- return value.str.map! (a => a == ' 1' ).array;
40+ static if (is (T == JSONValue))
41+ result = value;
42+ else static if (is (T == bool ) || is (T == string ) || isSomeChar! T || isNumeric! T || is (T == enum ))
43+ result = jsonValueToString(value).to! T;
44+ else static if (isSumType! T)
45+ {
46+ bool sumTypeDecoded = false ;
47+ static foreach (SumTypeVariant; T.Types)
48+ {
49+ {
50+ SumTypeVariant sumTypeResult;
51+ if (tryDeserializeJson! SumTypeVariant(value, sumTypeResult))
52+ {
53+ sumTypeDecoded = true ;
54+ result = sumTypeResult;
55+ }
5156 }
5257 }
53-
54- if (value.type != JSONType.array) {
55- return [value.fromJSON! (ForeachType! T)];
58+ if (! sumTypeDecoded)
59+ throw new Exception (" Failed to deserialize JSON value" );
60+ }
61+ else static if (isArray! T)
62+ {
63+ static if (isBoolean! (ForeachType! T))
64+ {
65+ if (value.type == JSONType.string && isBoolean! (ForeachType! T))
66+ result = value.str.map! (a => a == ' 1' ).array;
5667 }
68+ if (value.type != JSONType.array)
69+ result = [value.fromJSON! (ForeachType! T)];
70+ else
71+ result = value.array.map! (a => a.fromJSON! (ForeachType! T)).array;
5772
58- return value.array.map! (a => a.fromJSON! (ForeachType! T)).array;
5973 }
60- else static if (is (T == SysTime)) {
61- return SysTime.fromISOExtString(value.toString(JSONOptions.doNotEscapeSlashes).strip(" \" " ));
74+ else static if (is (T == SysTime))
75+ {
76+ result = SysTime.fromISOExtString(jsonValueToString(value));
6277 }
63- else static if (is (T == struct )) {
64- T result;
65- static foreach (idx, field; T.tupleof) {
66- if ((__traits(identifier, field).replace(" _" , " " ) in value.object) && ! value[__traits(identifier, field).replace(" _" , " " )].isNull) {
67- result.tupleof[idx] = value[__traits(identifier, field).replace(" _" , " " )].fromJSON! (typeof (field));
78+ else static if (is (T == struct ))
79+ {
80+ static foreach (idx, field; T.tupleof)
81+ {
82+ if ((__traits(identifier, field).replace(" _" , " " ) in value.object) && ! value[__traits(identifier, field)
83+ .replace(" _" , " " )].isNull)
84+ {
85+ result.tupleof[idx] = value[__traits(identifier, field)
86+ .replace(" _" , " " )].fromJSON! (typeof (field));
6887 }
6988 }
70- return result;
7189 }
72- else static if (isAssociativeArray! T) {
73- T result;
74- foreach (key, val; value.object) {
75- if (key in result) {
90+ else static if (isAssociativeArray! T)
91+ {
92+ foreach (key, val; value.object)
93+ {
94+ if (key in result)
7695 result[key] = val.fromJSON! (typeof (result[key]));
77- }
7896 }
79- return result;
80- }
81- else {
82- static assert (false , " Unsupported type: `" , T, " ` " , isSumType! T);
8397 }
98+ else
99+ static assert (false , " Unsupported type: `" , T, " ` " , isSumType! T);
100+ return result;
84101
85102}
86103
87104@(" fromJSON" )
88- unittest {
105+ unittest
106+ {
89107 auto x = fromJSON! (SumType! (int , string ))(JSONValue(" 1" ));
90108 auto y = fromJSON! (SumType! (int , string ))(JSONValue(1 ));
91109}
92110
93111JSONValue toJSON (T)(in T value, bool simplify = false )
94112{
113+ JSONValue result;
95114 static if (is (T == enum ))
96- {
97- return JSONValue (value.enumToString);
98- }
115+ result = JSONValue(value.enumToString);
99116 else static if (is (T == bool ) || is (T == string ) || isSomeChar! T || isNumeric! T)
100- return JSONValue (value);
101- else static if ((isArray! T && isSomeChar! (ForeachType! T)) ) {
102- return JSONValue (value.idup[0 .. (strlen(value.ptr)- 1 )]);
103- }
117+ result = JSONValue(value);
118+ else static if ((isArray! T && isSomeChar! (ForeachType! T)))
119+ result = JSONValue(value.idup[0 .. (strlen(value.ptr) - 1 )]);
104120 else static if (isArray! T)
105121 {
106122 if (simplify && value.length == 1 )
107- return value.front.toJSON(simplify);
108- else if (simplify && isBoolean! (ForeachType! T) ) {
109- static if (isBoolean! (ForeachType! T)) {
110- return JSONValue ((value.map! (a => a ? ' 1' : ' 0' ).array).to! string );
111- }
112- else {assert (0 );}
123+ result = value.front.toJSON(simplify);
124+ else if (simplify && isBoolean! (ForeachType! T))
125+ {
126+ static if (isBoolean! (ForeachType! T))
127+ result = JSONValue((value.map! (a => a ? ' 1' : ' 0' ).array).to! string );
128+ else
129+ assert (0 );
113130 }
114- else {
115- JSONValue[] result;
131+ else
132+ {
133+ JSONValue[] arrayResult;
116134 foreach (elem; value)
117- result ~= elem.toJSON(simplify);
118- return JSONValue (result );
135+ arrayResult ~= elem.toJSON(simplify);
136+ result = JSONValue(arrayResult );
119137 }
120138 }
121- else static if (is (T == SysTime)) {
122- return JSONValue (value.toISOExtString());
123- }
139+ else static if (is (T == SysTime))
140+ result = JSONValue(value.toISOExtString());
124141 else static if (is (T == struct ))
125142 {
126- JSONValue[string ] result ;
143+ JSONValue[string ] structResult ;
127144 auto name = " " ;
128145 static foreach (idx, field; T.tupleof)
129146 {
130147 name = __traits(identifier, field).strip(" _" );
131- result [name] = value.tupleof[idx].toJSON(simplify);
148+ structResult [name] = value.tupleof[idx].toJSON(simplify);
132149 }
133- return JSONValue (result );
150+ result = JSONValue(structResult );
134151 }
135152 else
136153 static assert (false , " Unsupported type: `" ~ __traits(identifier, T) ~ " `" );
154+
155+ return result;
156+
137157}
138158
139- version (unittest )
159+ version (unittest )
140160{
141161 enum TestEnum
142162 {
143- @StringRepresentation(" supercalifragilisticexpialidocious" )
144- a,
163+ @StringRepresentation(" supercalifragilisticexpialidocious" ) a,
145164 b,
146165 c
147166 }
167+
148168 struct TestStruct
149169 {
150170 int a;
151171 string b;
152172 bool c;
153173 }
174+
154175 struct TestStruct2
155176 {
156177 int a;
157178 TestStruct b;
158179 }
180+
159181 struct TestStruct3
160182 {
161183 int a;
@@ -166,18 +188,40 @@ version(unittest)
166188@(" toJSON" )
167189unittest
168190{
169- import std.stdio : writeln;
191+ import std.stdio : writeln;
192+
170193 assert (1. toJSON == JSONValue(1 ));
171194 assert (true .toJSON == JSONValue(true ));
172195 assert (" test" .toJSON == JSONValue(" test" ));
173196 assert ([1 , 2 , 3 ].toJSON == JSONValue([1 , 2 , 3 ]));
174197 assert ([" a" , " b" , " c" ].toJSON == JSONValue([" a" , " b" , " c" ]));
175- assert ([TestEnum.a, TestEnum.b, TestEnum.c].toJSON == JSONValue([" supercalifragilisticexpialidocious" , " b" , " c" ]));
176- TestStruct testStruct = { 1 , " test" , true };
177- assert (testStruct.toJSON == JSONValue([" a" : JSONValue(1 ), " b" : JSONValue(" test" ), " c" : JSONValue(true )]));
178- TestStruct2 testStruct2 = { 1 , testStruct };
179- assert (testStruct2.toJSON == JSONValue([" a" : JSONValue(1 ), " b" : JSONValue([" a" : JSONValue(1 ), " b" : JSONValue(" test" ), " c" : JSONValue(true )])]));
180- TestStruct3 testStruct3 = { 1 , testStruct2 };
181- assert (testStruct3.toJSON == JSONValue([" a" : JSONValue(1 ), " b" : JSONValue([" a" : JSONValue(1 ), " b" : JSONValue([" a" : JSONValue(1 ), " b" : JSONValue(" test" ), " c" : JSONValue(true )])])]));
198+ assert ([TestEnum.a, TestEnum.b, TestEnum.c].toJSON == JSONValue(
199+ [" supercalifragilisticexpialidocious" , " b" , " c" ]));
200+ TestStruct testStruct = {1 , " test" , true };
201+ assert (testStruct.toJSON == JSONValue([
202+ " a" : JSONValue(1 ),
203+ " b" : JSONValue(" test" ),
204+ " c" : JSONValue(true )
205+ ]));
206+ TestStruct2 testStruct2 = {1 , testStruct};
207+ assert (testStruct2.toJSON == JSONValue([
208+ " a" : JSONValue(1 ),
209+ " b" : JSONValue([
210+ " a" : JSONValue(1 ),
211+ " b" : JSONValue(" test" ),
212+ " c" : JSONValue(true )
213+ ])
214+ ]));
215+ TestStruct3 testStruct3 = {1 , testStruct2};
216+ assert (testStruct3.toJSON == JSONValue([
217+ " a" : JSONValue(1 ),
218+ " b" : JSONValue([
219+ " a" : JSONValue(1 ),
220+ " b" : JSONValue([
221+ " a" : JSONValue(1 ),
222+ " b" : JSONValue(" test" ),
223+ " c" : JSONValue(true )
224+ ])
225+ ])
226+ ]));
182227}
183-
0 commit comments