1 module text.json.Validation; 2 3 import std.conv; 4 import std.datetime; 5 import std.json; 6 import std..string; 7 import std.traits; 8 import std.typecons; 9 import text.time.Convert; 10 11 /** 12 * This value object represents a JSON object of name/value pairs. 13 * It is named `JSONObject` rather than `JsonObject` to comply with the naming of `JSONValue`. 14 * Its main purpose is to avoid conflicts with the `require` function introduced in D 2.082.0. 15 */ 16 public struct JSONObject 17 { 18 public JSONValue[string] attributes; 19 20 alias attributes this; 21 22 /** 23 * Throws: JSONException when the name is no attribute of the object. 24 */ 25 public inout(JSONValue) require(string name) inout pure @safe 26 { 27 if (name !in this.attributes) 28 { 29 throw new JSONException(format!`"%s" value required`(name)); 30 } 31 return this.attributes[name]; 32 } 33 34 /** 35 * Throws: JSONException when the name is no attribute of the object 36 * or when the value is not of the required type. 37 */ 38 public inout(T) require(T)(string name) inout 39 { 40 JSONValue value = require(name); 41 42 try 43 { 44 return value.require!T; 45 } 46 catch (JSONException exception) 47 { 48 throw new JSONException(format!`"%s" value: %s`(name, exception.msg)); 49 } 50 } 51 52 /** 53 * Throws: JSONException when the name is an attribute of the object but the value is not of the required type. 54 */ 55 public inout(T) require(T)(string name, lazy T fallback) inout 56 { 57 if (name !in this.attributes) 58 { 59 return fallback; 60 } 61 try 62 { 63 return this.attributes[name].require!T; 64 } 65 catch (JSONException exception) 66 { 67 throw new JSONException(format!`"%s" value: %s`(name, exception.msg)); 68 } 69 } 70 71 /** 72 * Throws: JSONException when the name is an attribute of the object but the value is not of the required type. 73 */ 74 public inout(Nullable!T) require(U : Nullable!T, T)(string name) inout 75 { 76 if (name !in this.attributes) 77 { 78 return Nullable!T(); 79 } 80 try 81 { 82 return Nullable!T(this.attributes[name].require!T); 83 } 84 catch (JSONException exception) 85 { 86 throw new JSONException(format!`"%s" value: %s`(name, exception.msg)); 87 } 88 } 89 } 90 91 /** 92 * Throws: JSONException when the value is not a JSON object. 93 */ 94 public JSONObject requireObject(JSONValue value) 95 { 96 if (value.type != JSONType.object) 97 { 98 throw new JSONException(format!"object required but got %s"(value)); 99 } 100 return JSONObject(value.object); 101 } 102 103 /** 104 * Throws: JSONException when the value is not a JSON object with values of the required type. 105 */ 106 public T[string] requireObject(T)(JSONValue object) 107 { 108 T[string] array = null; 109 110 foreach (name, value; object.requireObject) 111 { 112 array[name] = value.require!T; 113 } 114 115 return array; 116 } 117 118 /** 119 * Throws: JSONException when the value is not a JSON array. 120 */ 121 public JSONValue[] requireArray(JSONValue value) 122 { 123 if (value.type != JSONType.array) 124 { 125 throw new JSONException(format!"array required but got %s"(value)); 126 } 127 return value.array; 128 } 129 130 /** 131 * Throws: JSONException when the value is not a JSON array with elements of the required type. 132 */ 133 public T[] requireArray(T)(JSONValue value) 134 { 135 T[] array = null; 136 137 foreach (element; value.requireArray) 138 { 139 array ~= element.require!T; 140 } 141 return array; 142 } 143 144 /** 145 * Throws: JSONException when the value is not a boolean. 146 */ 147 public T require(T)(JSONValue value) 148 if (is(T == bool)) 149 { 150 if (value.type != JSONType.true_ && value.type != JSONType.false_) 151 { 152 throw new JSONException(format!"boolean required but got %s"(value)); 153 } 154 return value.type == JSONType.true_; 155 } 156 157 /** 158 * Throws: JSONException when the value is not a number or when the number is not of the required type. 159 */ 160 public T require(T)(JSONValue value) 161 if (isIntegral!T && !is(T == enum)) 162 { 163 try 164 { 165 switch (value.type) 166 { 167 case JSONType.integer: 168 return value.integer.to!T; 169 case JSONType.uinteger: 170 return value.uinteger.to!T; 171 default: 172 throw new JSONException(format!"integral value required but got %s"(value)); 173 } 174 } 175 catch (ConvException) 176 { 177 throw new JSONException(format!"%s is not a valid value of type %s"(value, T.stringof)); 178 } 179 } 180 181 /** 182 * Throws: JSONException when the value is not of the required float type. 183 */ 184 public T require(T)(JSONValue value) 185 if (isFloatingPoint!T) 186 { 187 switch (value.type) 188 { 189 case JSONType.integer: 190 return value.integer.to!T; 191 case JSONType.uinteger: 192 return value.uinteger.to!T; 193 case JSONType.float_: 194 return value.floating.to!T; 195 default: 196 throw new JSONException(format!"numeric value required but got %s"(value)); 197 } 198 } 199 200 /** 201 * Throws: JSONException when the value is not a string. 202 */ 203 public T require(T : string)(JSONValue value) 204 if (!is(T == enum)) 205 { 206 if (value.type != JSONType..string) 207 { 208 throw new JSONException(format!"string required but got %s"(value)); 209 } 210 return value.str; 211 } 212 213 /** 214 * Throws: JSONException when the value is not a member of the required enumeration. 215 */ 216 public T require(T)(JSONValue value) 217 if (is(T == enum) && !is(T : bool)) 218 { 219 try 220 { 221 return value.require!string.to!T; 222 } 223 catch (ConvException) 224 { 225 throw new JSONException(format!"%s required but got %s"(T.stringof, value)); 226 } 227 } 228 229 /** 230 * Throws: JSONException when the value is not a boolean. 231 */ 232 public T require(T)(JSONValue value) 233 // Flag!"Name" is enum Flag : bool 234 if (is(T == enum) && is(T : bool)) 235 { 236 return value.require!bool ? T.yes : T.no; 237 } 238 239 /** 240 * Throws: JSONException when the value is not of the required date-time type. 241 */ 242 public T require(T)(JSONValue value) 243 if (is(T == Date) || is(T == Duration) || is(T == SysTime) || is(T == TimeOfDay)) 244 { 245 try 246 { 247 return Convert.to!T(value.require!string); 248 } 249 catch (DateTimeException exception) 250 { 251 throw new JSONException(exception.msg); 252 } 253 }