1 /** 2 * Deserialization utilities 3 */ 4 module jstruct.deserializer; 5 6 import std.json; 7 import jstruct.exceptions : DeserializationError; 8 import std.traits : FieldTypeTuple, FieldNameTuple, isArray, ForeachType, EnumMembers, fullyQualifiedName;; 9 import std.conv : to; 10 11 /** 12 * Deserializes the provided JSON into a struct of type RecordType 13 * 14 * Params: 15 * jsonIn = the JSON to deserialize 16 * Throws: 17 * RemoteFieldMissing = if the field names in the provided RecordType 18 * cannot be found within the prpvided JSONValue `jsonIn`. 19 * Returns: an instance of RecordType 20 */ 21 public RecordType fromJSON(RecordType)(JSONValue jsonIn) 22 { 23 RecordType record; 24 25 // Alias as to only expand later when used in compile-time 26 alias structTypes = FieldTypeTuple!(RecordType); 27 alias structNames = FieldNameTuple!(RecordType); 28 alias structValues = record.tupleof; 29 30 static foreach(cnt; 0..structTypes.length) 31 { 32 debug(dbg) 33 { 34 pragma(msg, structTypes[cnt]); 35 pragma(msg, structNames[cnt]); 36 // pragma(msg, structValues[cnt]); 37 } 38 39 debug(dbg) 40 { 41 pragma(msg, "Bruh type"); 42 pragma(msg, structTypes[cnt]); 43 // pragma(msg, __traits(identifier, mixin(structTypes[cnt]))); 44 } 45 46 try 47 { 48 static if(__traits(isSame, structTypes[cnt], byte)) 49 { 50 mixin("record."~structNames[cnt]) = cast(byte)jsonIn[structNames[cnt]].integer(); 51 } 52 else static if(__traits(isSame, structTypes[cnt], ubyte)) 53 { 54 mixin("record."~structNames[cnt]) = cast(ubyte)jsonIn[structNames[cnt]].uinteger(); 55 } 56 else static if(__traits(isSame, structTypes[cnt], short)) 57 { 58 mixin("record."~structNames[cnt]) = cast(short)jsonIn[structNames[cnt]].integer(); 59 } 60 else static if(__traits(isSame, structTypes[cnt], ushort)) 61 { 62 mixin("record."~structNames[cnt]) = cast(ushort)jsonIn[structNames[cnt]].uinteger(); 63 } 64 else static if(__traits(isSame, structTypes[cnt], int)) 65 { 66 mixin("record."~structNames[cnt]) = cast(int)jsonIn[structNames[cnt]].integer(); 67 } 68 else static if(__traits(isSame, structTypes[cnt], uint)) 69 { 70 mixin("record."~structNames[cnt]) = cast(uint)jsonIn[structNames[cnt]].uinteger(); 71 } 72 else static if(__traits(isSame, structTypes[cnt], ulong)) 73 { 74 mixin("record."~structNames[cnt]) = cast(ulong)jsonIn[structNames[cnt]].uinteger(); 75 } 76 else static if(__traits(isSame, structTypes[cnt], long)) 77 { 78 mixin("record."~structNames[cnt]) = cast(long)jsonIn[structNames[cnt]].integer(); 79 } 80 else static if(__traits(isSame, structTypes[cnt], string)) 81 { 82 mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].str(); 83 84 debug(dbg) 85 { 86 pragma(msg,"record."~structNames[cnt]); 87 } 88 } 89 else static if(__traits(isSame, structTypes[cnt], JSONValue)) 90 { 91 mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]]; 92 93 debug(dbg) 94 { 95 pragma(msg,"record."~structNames[cnt]); 96 } 97 } 98 else static if(__traits(isSame, structTypes[cnt], bool)) 99 { 100 mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean(); 101 102 debug(dbg) 103 { 104 pragma(msg,"record."~structNames[cnt]); 105 } 106 } 107 else static if(isArray!(structTypes[cnt])) 108 { 109 alias recordArrayComponent = mixin("record."~structNames[cnt]); 110 111 JSONValue[] jsonArray = jsonIn[structNames[cnt]].array(); 112 113 for(ulong i = 0; i < jsonArray.length; i++) 114 { 115 JSONValue jsonVal = jsonArray[i]; 116 117 static if(__traits(isSame, ForeachType!(structTypes[cnt]), byte)) 118 { 119 mixin("record."~structNames[cnt])~= cast(byte)jsonVal.integer(); 120 } 121 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), ubyte)) 122 { 123 mixin("record."~structNames[cnt])~= cast(ubyte)jsonVal.uinteger(); 124 } 125 static if(__traits(isSame, ForeachType!(structTypes[cnt]), short)) 126 { 127 mixin("record."~structNames[cnt])~= cast(short)jsonVal.integer(); 128 } 129 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), ushort)) 130 { 131 mixin("record."~structNames[cnt])~= cast(ushort)jsonVal.uinteger(); 132 } 133 static if(__traits(isSame, ForeachType!(structTypes[cnt]), int)) 134 { 135 mixin("record."~structNames[cnt])~= cast(int)jsonVal.integer(); 136 } 137 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), uint)) 138 { 139 mixin("record."~structNames[cnt])~= cast(uint)jsonVal.uinteger(); 140 } 141 static if(__traits(isSame, ForeachType!(structTypes[cnt]), long)) 142 { 143 mixin("record."~structNames[cnt])~= cast(long)jsonVal.integer(); 144 } 145 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), ulong)) 146 { 147 mixin("record."~structNames[cnt])~= cast(ulong)jsonVal.uinteger(); 148 } 149 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), bool)) 150 { 151 mixin("record."~structNames[cnt])~= jsonVal.boolean(); 152 } 153 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), float)) 154 { 155 mixin("record."~structNames[cnt])~= cast(float)jsonVal.floating(); 156 } 157 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), double)) 158 { 159 mixin("record."~structNames[cnt])~= cast(double)jsonVal.floating(); 160 } 161 else static if(__traits(isSame, ForeachType!(structTypes[cnt]), string)) 162 { 163 mixin("record."~structNames[cnt])~= jsonVal.str(); 164 } 165 166 167 168 } 169 170 171 // mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].array(); 172 173 debug(dbg) 174 { 175 pragma(msg,"record."~structNames[cnt]); 176 } 177 } 178 else static if(is(structTypes[cnt] == enum)) 179 { 180 string enumChoice = jsonIn[structNames[cnt]].str(); 181 182 alias members = EnumMembers!(structTypes[cnt]); 183 184 version(dbg) 185 { 186 import std.stdio : writeln; 187 } 188 189 static foreach(member; members) 190 { 191 version(dbg) 192 { 193 writeln(member); 194 writeln(fullyQualifiedName!(member)); 195 writeln(__traits(identifier, member)); 196 } 197 198 if(__traits(identifier, member) == enumChoice) 199 { 200 mixin("record."~structNames[cnt]) = member; 201 } 202 } 203 } 204 else 205 { 206 // throw new 207 //TODO: Throw error 208 debug(dbg) 209 { 210 pragma(msg, "Unknown type for de-serialization"); 211 212 pragma(msg, is(structTypes[cnt] == enum)); 213 214 215 } 216 } 217 } 218 catch(JSONException e) 219 { 220 throw new DeserializationError(); 221 } 222 } 223 224 return record; 225 } 226 227 /** 228 * Example deserialization of JSON 229 * to our `Person` struct 230 */ 231 unittest 232 { 233 enum EnumType 234 { 235 DOG, 236 CAT 237 } 238 239 struct Person 240 { 241 public string firstname, lastname; 242 public int age; 243 public bool isMale; 244 public JSONValue obj; 245 public int[] list; 246 public bool[] list2; 247 public float[] list3; 248 public double[] list4; 249 public string[] list5; 250 public EnumType animal; 251 } 252 253 JSONValue json = parseJSON(`{ 254 "firstname" : "Tristan", 255 "lastname": "Kildaire", 256 "age": 23, 257 "obj" : {"bruh":1}, 258 "isMale": true, 259 "list": [1,2,3], 260 "list2": [true, false], 261 "list3": [1.5, 1.4], 262 "list4": [1.5, 1.4], 263 "list5": ["baba", "booey"], 264 "animal": "CAT" 265 } 266 `); 267 268 Person person = fromJSON!(Person)(json); 269 270 debug(dbg) 271 { 272 writeln("Deserialized as: ", person); 273 } 274 275 assert(cmp(person.firstname, "Tristan") == 0); 276 assert(cmp(person.lastname, "Kildaire") == 0); 277 assert(person.age == 23); 278 assert(person.isMale == true); 279 assert(person.obj["bruh"].integer() == 1); 280 assert(person.list == [1,2,3]); 281 assert(person.list2 == [true, false]); 282 assert(person.list3 == [1.5F, 1.4F]); 283 assert(person.list4 == [1.5, 1.4]); 284 assert(person.list5 == ["baba", "booey"]); 285 assert(person.animal == EnumType.CAT); 286 287 } 288 289 version(unittest) 290 { 291 import std.string : cmp; 292 import std.stdio : writeln; 293 } 294 295 /** 296 * Another example deserialization of JSON 297 * to our `Person` struct but here there 298 * is a problem with deserialization as 299 * there is a missing field `isMale` 300 * in the provided JSON 301 */ 302 unittest 303 { 304 struct Person 305 { 306 public string firstname, lastname; 307 public int age; 308 public bool isMale; 309 public JSONValue obj; 310 public int[] list; 311 312 } 313 314 JSONValue json = parseJSON(`{ 315 "firstname" : "Tristan", 316 "lastname": "Kildaire", 317 "age": 23, 318 "obj" : {"bruh":1}, 319 "list": [1,2,3] 320 } 321 `); 322 323 try 324 { 325 Person person = fromJSON!(Person)(json); 326 assert(false); 327 } 328 catch(DeserializationError) 329 { 330 assert(true); 331 } 332 333 } 334 335 336 unittest 337 { 338 enum EnumType 339 { 340 DOG, 341 CAT 342 } 343 344 struct Person 345 { 346 public string firstname, lastname; 347 348 349 public EnumType animal; 350 351 } 352 353 JSONValue json = parseJSON(`{ 354 "firstname" : "Tristan", 355 "lastname": "Kildaire", 356 "animal" : "CAT" 357 } 358 `); 359 360 try 361 { 362 Person person = fromJSON!(Person)(json); 363 writeln(person); 364 assert(true); 365 } 366 catch(DeserializationError) 367 { 368 assert(false); 369 } 370 371 }