1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.text.JsonHelper; 13 14 import std.algorithm : map; 15 import std.array; 16 import std.conv; 17 import std.datetime; 18 import std.json; 19 import std.stdio; 20 import std.traits; 21 // import std.typecons; 22 23 import hunt.logging; 24 // import hunt.util.serialize; 25 26 final class JsonHelper { 27 28 static T getItemAs(T, bool canThrow = false)(ref const(JSONValue) json, string name, 29 T defaultValue = T.init) if (!is(T == void)) { 30 JSONType jt = json.type(); 31 if (jt != JSON_TYPE.OBJECT) { 32 return handleException!(T, canThrow)(json, "wrong member type", defaultValue); 33 } 34 35 auto item = name in json; 36 if (item is null) { 37 return handleException!(T, canThrow)(json, "wrong member type", defaultValue); 38 } 39 else { 40 return getAs!T(*item); // , defaultValue 41 } 42 } 43 44 /** 45 Converts a `JSONValue` to an object of type `T` by filling its fields with the JSON's fields. 46 */ 47 static T getAs(T, bool canThrow = false)(auto ref const(JSONValue) json, 48 T defaultValue = T.init) if ((is(T == class) || is(T == struct)) && !is(T == SysTime)) { 49 JSONType jt = json.type(); 50 51 if (jt != JSON_TYPE.OBJECT) { 52 return handleException!(T, canThrow)(json, "wrong object type", defaultValue); 53 } 54 55 static if (is(T == class)) { 56 auto result = new T(); 57 } 58 else { 59 auto result = T(); 60 } 61 62 foreach (member; __traits(allMembers, T)) { 63 static if (__traits(getProtection, __traits(getMember, T, 64 member)) == "public" && !isType!(__traits(getMember, T, 65 member)) && !isSomeFunction!(__traits(getMember, T, member))) { 66 try { 67 __traits(getMember, result, member) = getAs!(typeof(__traits(getMember, 68 result, member)), canThrow)(json[member]); 69 } 70 catch (JSONException e) { 71 return handleException!(T, canThrow)(json, e.msg, defaultValue); 72 } 73 } 74 } 75 76 return result; 77 } 78 79 static T getAs(T, bool canThrow = false)(auto ref const(JSONValue) json, 80 T defaultValue = T.init) if(is(T == SysTime)) { 81 82 JSONType jt = json.type(); 83 if(jt == JSON_TYPE..string) { 84 return SysTime.fromSimpleString(json.str); 85 } else if(jt == JSON_TYPE.INTEGER) { 86 return SysTime(json.integer); // STD time 87 } else { 88 return handleException!(T, canThrow)(json, "wrong SysTime type", defaultValue); 89 } 90 } 91 92 // static N getAs(N : Nullable!T, T, bool canThrow = false)(auto ref const(JSONValue) json) { 93 94 // return (json.type == JSON_TYPE.NULL) ? N() : getAs!T(json).nullable; 95 // } 96 97 static T getAs(T : JSONValue, bool canThrow = false)(auto ref const(JSONValue) json) { 98 import std.typecons : nullable; 99 return json.nullable; 100 } 101 102 static T getAs(T, bool canThrow = false)(auto ref const(JSONValue) json, T defaultValue = T.init) 103 if (isNumeric!T || isSomeChar!T) { 104 105 switch (json.type) { 106 case JSON_TYPE.NULL, JSON_TYPE.FALSE: 107 return 0.to!T; 108 109 case JSON_TYPE.TRUE: 110 return 1.to!T; 111 112 case JSON_TYPE.FLOAT: 113 return json.floating.to!T; 114 115 case JSON_TYPE.INTEGER: 116 return json.integer.to!T; 117 118 case JSON_TYPE.UINTEGER: 119 return json.uinteger.to!T; 120 121 case JSON_TYPE.STRING: 122 try { 123 return json.str.to!T; 124 } catch(Exception ex) { 125 return handleException!(T, canThrow)(json, ex.msg, defaultValue); 126 } 127 128 default: 129 return handleException!(T, canThrow)(json, "", defaultValue); 130 } 131 } 132 133 static T handleException(T, bool canThrow = false) (auto ref const(JSONValue) json, 134 string message, T defaultValue = T.init) { 135 static if (canThrow) { 136 throw new JSONException(json.toString() ~ " is not a " ~ T.stringof ~ " type"); 137 } else { 138 version (HUNT_DEBUG) 139 warningf(" %s is not a %s type. Using the defaults instead! \n Exception: %s", 140 json.toString(), T.stringof, message); 141 return defaultValue; 142 } 143 } 144 145 static T getAs(T, bool canThrow = false)(auto ref const(JSONValue) json) if (isBoolean!T) { 146 import std.json : JSON_TYPE; 147 148 switch (json.type) { 149 case JSON_TYPE.NULL, JSON_TYPE.FALSE: 150 return false; 151 152 case JSON_TYPE.FLOAT: 153 return json.floating != 0; 154 155 case JSON_TYPE.INTEGER: 156 return json.integer != 0; 157 158 case JSON_TYPE.UINTEGER: 159 return json.uinteger != 0; 160 161 case JSON_TYPE.STRING: 162 return json.str.length > 0; 163 164 default: 165 return true; 166 } 167 } 168 169 static T getAs(T, bool canThrow = false)(auto ref const(JSONValue) json, T defaultValue = T.init) 170 if (isSomeString!T || is(T : string) || is(T : wstring) || is(T : dstring)) { 171 172 static if (is(T == enum)) { 173 foreach (member; __traits(allMembers, T)) { 174 auto m = __traits(getMember, T, member); 175 176 if (json.str == m) { 177 return m; 178 } 179 } 180 return handleException!(T, canThrow)(json, 181 " is not a member of " ~ typeid(T).toString(), defaultValue); 182 } else { 183 return (json.type == JSON_TYPE.STRING ? json.str : json.toString()).to!T; 184 } 185 } 186 187 static T getAs(T : U[], bool canThrow = false, U)(auto ref const(JSONValue) json, T defaultValue = T.init) 188 if (isArray!T && !isSomeString!T && !is(T : string) && !is(T 189 : wstring) && !is(T : dstring)) { 190 191 switch (json.type) { 192 case JSON_TYPE.NULL: 193 return []; 194 195 case JSON_TYPE.FALSE: 196 return [getAs!U(JSONValue(false))]; 197 198 case JSON_TYPE.TRUE: 199 return [getAs!U(JSONValue(true))]; 200 201 case JSON_TYPE.ARRAY: 202 return json.array 203 .map!(value => getAs!U(value)) 204 .array 205 .to!T; 206 207 case JSON_TYPE.OBJECT: 208 // throw new JSONException(json.toString() ~ " is not a string type"); 209 return handleException!(T, canThrow)(json, "", defaultValue); 210 211 default: 212 return [getAs!U(json)]; 213 } 214 } 215 216 static T getAs(T : U[K], bool canThrow = false, U, K)(auto ref const(JSONValue) json, T defaultValue = T.init) 217 if (isAssociativeArray!T) { 218 U[K] result; 219 220 switch (json.type) { 221 case JSON_TYPE.NULL: 222 return result; 223 224 case JSON_TYPE.OBJECT: 225 foreach (key, value; json.object) { 226 result[key.to!K] = getAs!U(value); 227 } 228 229 break; 230 231 case JSON_TYPE.ARRAY: 232 foreach (key, value; json.array) { 233 result[key.to!K] = getAs!U(value); 234 } 235 236 break; 237 238 default: 239 // throw new JSONException(json.toString() ~ " is not an object type"); 240 return handleException!(T, canThrow)(json, "", defaultValue); 241 } 242 243 return result; 244 } 245 246 /// toJson 247 248 // Nullable!JSONValue toJson(T)(T value) { 249 // return JSONValue.init; 250 // return toJson(t, level, ignore); 251 // } 252 253 static JSONValue toJson(T)(T value, uint level = uint.max, bool ignore = true) 254 if ((is(T == class) || is(T == struct)) && !is(T == JSONValue) && !is(T == SysTime)) { 255 import std.traits : isSomeFunction, isType; 256 // import std.typecons : nullable; 257 258 static if (is(T == class)) { 259 if (value is null) { 260 return JSONValue(null); 261 } 262 } 263 264 auto result = JSONValue(); 265 266 foreach (member; __traits(allMembers, T)) { 267 static if (__traits(getProtection, __traits(getMember, T, 268 member)) == "public" && !isType!(__traits(getMember, T, 269 member)) && !isSomeFunction!(__traits(getMember, T, member))) { 270 auto json = toJson!(typeof(__traits(getMember, value, member)))( 271 __traits(getMember, value, member)); 272 273 if (!json.isNull) { 274 result[member] = json; 275 } 276 } 277 } 278 279 return result; 280 } 281 282 283 static JSONValue toJson(T)(T value, bool asInteger=true) if(is(T == SysTime)) { 284 if(asInteger) 285 return JSONValue(value.stdTime()); // STD time 286 else 287 return JSONValue(value.toString()); 288 } 289 290 // static Nullable!JSONValue toJson(N : Nullable!T, T)(N value) { 291 // return value.isNull ? Nullable!JSONValue() : Nullable!JSONValue(toJson!T(value.get())); 292 // } 293 294 static JSONValue toJson(T)(T value) 295 if ((!is(T == class) && !is(T == struct)) || is(T == JSONValue)) { 296 return JSONValue(value); 297 } 298 299 static JSONValue toJson(T : U[], U)(T value) 300 if (isArray!T && !isSomeString!T && !is(T : string) && !is(T : wstring) && !is(T : dstring)) { 301 import std.algorithm : map; 302 303 return JSONValue(value.map!(item => toJson(item))() 304 .map!(json => json.isNull ? JSONValue(null) : json).array); 305 } 306 307 static JSONValue toJson(T : U[K], U, K)(T value) if (isAssociativeArray!T) { 308 auto result = JSONValue(); 309 310 foreach (key; value.keys) { 311 auto json = toJson(value[key]); 312 result[key.to!string] = json.isNull ? JSONValue(null) : json; 313 } 314 315 return result; 316 } 317 318 }