1 /** 2 * Copyright © Yurai Web Framework 2021 3 * License: MIT (https://github.com/YuraiWeb/yurai/blob/main/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module yurai.json.serialization; 7 8 import std.traits : hasUDA, FieldNameTuple, getUDAs, moduleName, isSomeString, isScalarType, isArray, isAssociativeArray; 9 import std.string : format; 10 import std.algorithm : map; 11 import std.array : join, array; 12 13 import yurai.json.parser; 14 import yurai.json.jsonobject; 15 import yurai.json.jsontype; 16 import yurai.core.meta; 17 18 struct JsonIgnore {} 19 20 struct JsonRequired {} 21 22 struct JsonField { string fieldName; } 23 24 struct JsonRead { string handler; } 25 26 struct JsonWrite { string handler; } 27 28 T deserializeJson(T,S = string)(S jsonString) 29 if (isSomeString!S) 30 { 31 T value; 32 S[] errorMessages; 33 34 if (deserializeJsonSafe(jsonString, value, errorMessages)) 35 { 36 return value; 37 } 38 39 return T.init; 40 } 41 42 bool deserializeJsonSafe(T,S = string)(S jsonString, out T value, out S[] errorMessages) 43 if (isSomeString!S) 44 { 45 Json!S json; 46 47 if (!parseJsonSafe(jsonString, json, errorMessages)) 48 { 49 value = T.init; 50 return false; 51 } 52 53 return deserializeJsonObjectSafe(json, value, errorMessages); 54 } 55 56 T deserializeJsonObject(T,S = string)(Json!S json) 57 { 58 T value; 59 S[] errorMessages; 60 61 if (deserializeJsonObjectSafe(json, value, errorMessages)) 62 { 63 return value; 64 } 65 66 return T.init; 67 } 68 69 bool deserializeJsonObjectSafe(T,S = string)(Json!S json, out T value, out S[] errorMessages) 70 { 71 static if (is(T == class) || is(T == struct)) 72 { 73 if (json.jsonType == JsonType.jsonNull) 74 { 75 value = T.init; 76 } 77 else if (json.jsonType != JsonType.jsonObject) 78 { 79 errorMessages ~= "Value is not an object."; 80 return false; 81 } 82 else 83 { 84 static if (is(T == class)) 85 { 86 value = new T; 87 } 88 else 89 { 90 value = T.init; 91 } 92 93 mixin("import " ~ moduleName!T ~ ";"); 94 95 const memberFormat = q{ 96 { 97 %s 98 } 99 }; 100 101 mixin HandleFields!(T, q{{ 102 enum hasJsonIgnore = hasUDA!({{fullName}}, JsonIgnore); 103 104 static if (!hasJsonIgnore) 105 { 106 enum hasJsonRequired = hasUDA!({{fullName}}, JsonRequired); 107 108 { 109 enum hasJsonField = hasUDA!({{fullName}}, JsonField); 110 111 static if (hasJsonField) 112 { 113 mixin("enum rawJsonFieldAttribute = getUDAs!(%s, JsonField)[0];".format("{{fullName}}")); 114 115 const S rawFieldName = rawJsonFieldAttribute.fieldName; 116 } 117 else 118 { 119 const S rawFieldName = "{{fieldName}}"; 120 } 121 122 Json!S rawMember; 123 if (json.getMember(rawFieldName, rawMember)) 124 { 125 enum hasJsonRead = hasUDA!({{fullName}}, JsonRead); 126 127 static if (hasJsonRead) 128 { 129 mixin("enum rawJsonReadAttribute = getUDAs!(%s, JsonRead)[0];".format("{{fullName}}")); 130 131 mixin("value." ~ rawJsonReadAttribute.handler ~ "(rawMember);"); 132 } 133 else 134 { 135 typeof({{fullName}}) rawValue; 136 if (deserializeJsonObjectSafe!(typeof(rawValue))(rawMember, rawValue, errorMessages)) 137 { 138 value.{{fieldName}} = rawValue; 139 } 140 else if (hasJsonRequired) 141 { 142 errorMessages ~= "Requried field from json has invalid type or value: %s".format("{{fieldName}}"); 143 } 144 } 145 } 146 else if (hasJsonRequired) 147 { 148 errorMessages ~= "Requried field missing from json: %s".format("{{fieldName}}"); 149 } 150 } 151 } 152 }}); 153 154 mixin(memberFormat.format(handleThem())); 155 } 156 157 return !errorMessages || !errorMessages.length; 158 } 159 else static if (isSomeString!T) 160 { 161 static if (is(T == S)) 162 { 163 if (!json.getText(value)) 164 { 165 errorMessages ~= "Value is not a string of type %s.".format(S.stringof); 166 return false; 167 } 168 169 return !errorMessages || !errorMessages.length; 170 } 171 else 172 { 173 errorMessages ~= "Value is not a string of type %s.".format(S.stringof); 174 return false; 175 } 176 } 177 else static if (isArray!T) 178 { 179 if (json.jsonType == JsonType.jsonNull) 180 { 181 value = T.init; 182 } 183 else 184 { 185 Json!S[] items; 186 if (!json.getItems(items)) 187 { 188 errorMessages ~= "Missing items from json object."; 189 return false; 190 } 191 192 foreach (item; items) 193 { 194 import std.range.primitives : ElementType; 195 196 ElementType!T itemValue; 197 if (deserializeJsonObjectSafe!(typeof(itemValue))(item, itemValue, errorMessages)) 198 { 199 value ~= itemValue; 200 } 201 } 202 } 203 204 return !errorMessages || !errorMessages.length; 205 } 206 else static if (isAssociativeArray!T) 207 { 208 static if (!is(ArrayElementType!(typeof(T.init.keys)) == S)) 209 { 210 errorMessages ~= "Associative Array key is not a string of type %s.".format(S.stringof); 211 return false; 212 } 213 else static if (isSomeString!(ArrayElementType!(typeof(T.init.values))) && !is(ArrayElementType!(typeof(T.init.values)) == S)) 214 { 215 errorMessages ~= "Associative Array value is not a string of type %s.".format(S.stringof); 216 return false; 217 } 218 else 219 { 220 if (json.jsonType == JsonType.jsonNull) 221 { 222 value = T.init; 223 } 224 else 225 { 226 Json!S[S] members; 227 if (!json.getMembers(members)) 228 { 229 errorMessages ~= "Missing members from json object."; 230 return false; 231 } 232 233 foreach (key,member; members) 234 { 235 ArrayElementType!(typeof(T.init.values)) memberValue; 236 if (deserializeJsonObjectSafe!(typeof(memberValue))(member, memberValue, errorMessages)) 237 { 238 value[key] = memberValue; 239 } 240 } 241 } 242 243 return !errorMessages || !errorMessages.length; 244 } 245 } 246 else static if (is(T == bool)) 247 { 248 if (!json.getBoolean(value)) 249 { 250 errorMessages ~= "Value is not a boolean."; 251 return false; 252 } 253 254 return !errorMessages || !errorMessages.length; 255 } 256 else static if (isScalarType!T) 257 { 258 if (!json.getNumber(value)) 259 { 260 errorMessages ~= "Value is not a number."; 261 return false; 262 } 263 264 return !errorMessages || !errorMessages.length; 265 } 266 else 267 { 268 errorMessages ~= "Undefined value."; 269 return false; 270 } 271 } 272 273 S serializeJson(T,S = string)(T value, bool pretty = false) 274 { 275 S jsonString; 276 if (serializeJsonSafe(value, jsonString, pretty)) 277 { 278 return jsonString; 279 } 280 281 return null; 282 } 283 284 bool serializeJsonSafe(T,S = string)(T value, out S jsonString, bool pretty = false) 285 { 286 Json!S json; 287 if (!serializeJsonObjectSafe(value, json)) 288 { 289 jsonString = null; 290 return false; 291 } 292 293 if (pretty) 294 { 295 jsonString = json.toPrettyString; 296 } 297 else 298 { 299 jsonString = json.toString; 300 } 301 302 return true; 303 } 304 305 Json!S serializeJsonObject(T,S = string)(T value) 306 { 307 Json!S json; 308 if (serializeJsonObjectSafe(value, json)) 309 { 310 return json; 311 } 312 313 return null; 314 } 315 316 bool serializeJsonObjectSafe(T,S = string)(T value, out Json!S json) 317 { 318 json = new Json!S; 319 320 static if (is(T == class) || is(T == struct)) 321 { 322 static if (is(T == class)) 323 { 324 if (value is null) 325 { 326 json.jsonType = JsonType.jsonNull; 327 return true; 328 } 329 } 330 331 mixin("import " ~ moduleName!T ~ ";"); 332 333 const memberFormat = q{ 334 { 335 %s 336 } 337 }; 338 339 mixin HandleFields!(T, q{{ 340 enum hasJsonIgnore = hasUDA!({{fullName}}, JsonIgnore); 341 342 static if (!hasJsonIgnore) 343 { 344 { 345 enum hasJsonField = hasUDA!({{fullName}}, JsonField); 346 347 static if (hasJsonField) 348 { 349 mixin("enum rawJsonFieldAttribute = getUDAs!(%s, JsonField)[0];".format("{{fullName}}")); 350 351 const S rawFieldName = rawJsonFieldAttribute.fieldName; 352 } 353 else 354 { 355 const S rawFieldName = "{{fieldName}}"; 356 } 357 358 enum hasJsonWrite = hasUDA!({{fullName}}, JsonWrite); 359 360 static if (hasJsonWrite) 361 { 362 mixin("enum rawJsonWriteAttribute = getUDAs!(%s, JsonWrite)[0];".format("{{fullName}}")); 363 364 mixin("Json!S memberJson = value." ~ rawJsonWriteAttribute.handler ~ "(new Json!S);"); 365 366 if (memberJson) 367 { 368 json.addMember(rawFieldName, memberJson); 369 } 370 } 371 else 372 { 373 Json!S memberJson; 374 if (serializeJsonObjectSafe(value.{{fieldName}}, memberJson)) 375 { 376 json.addMember(rawFieldName, memberJson); 377 } 378 } 379 } 380 } 381 }}); 382 383 mixin(memberFormat.format(handleThem())); 384 385 return true; 386 } 387 else static if (isSomeString!T) 388 { 389 static if (is(T == S)) 390 { 391 json.setText(value); 392 return true; 393 } 394 else 395 { 396 return false; 397 } 398 } 399 else static if (isArray!T) 400 { 401 if (!value || !value.length) 402 { 403 json.jsonType = JsonType.jsonArray; 404 } 405 else 406 { 407 foreach (item; value) 408 { 409 Json!S itemJson; 410 if (serializeJsonObjectSafe(item, itemJson)) 411 { 412 json.addItem(itemJson); 413 } 414 } 415 } 416 417 return true; 418 } 419 else static if (isAssociativeArray!T) 420 { 421 static if (!is(ArrayElementType!(typeof(T.init.keys)) == S)) 422 { 423 return false; 424 } 425 else static if (isSomeString!(ArrayElementType!(typeof(T.init.values))) && !is(ArrayElementType!(typeof(T.init.values)) == S)) 426 { 427 return false; 428 } 429 else 430 { 431 if (!value) 432 { 433 json.jsonType = JsonType.jsonNull; 434 } 435 else 436 { 437 foreach (key,member; value) 438 { 439 Json!S memberJson; 440 if (serializeJsonObjectSafe(member, memberJson)) 441 { 442 json.addMember(key, memberJson); 443 } 444 } 445 } 446 447 return true; 448 } 449 } 450 else static if (is(T == bool)) 451 { 452 json.setBoolean(value); 453 return true; 454 } 455 else static if (isScalarType!T) 456 { 457 json.setNumber(value); 458 return true; 459 } 460 else 461 { 462 return false; 463 } 464 }