1 /* 2 * Medea: Modern JSON for D 3 */ 4 module medea; 5 6 import std.json; 7 import std.traits; 8 import std.conv : text; 9 10 abstract class Value { 11 public: 12 JSONValue toJSONValue(); 13 } 14 15 string toJSONString(Value value, bool pretty = false) { 16 JSONValue jval = value.toJSONValue(); 17 return (&jval).toJSON(pretty); 18 } 19 20 abstract class NumberValue : Value { 21 22 } 23 24 final class IntegerValue : NumberValue { 25 private: 26 long _n; 27 public: 28 this(in long n) { 29 _n = n; 30 } 31 32 this() { 33 this(0); 34 } 35 36 override JSONValue toJSONValue() { 37 JSONValue v; 38 v.integer = _n; 39 return v; 40 } 41 @property long number() { 42 return _n; 43 } 44 } 45 46 final class UIntegerValue : NumberValue { 47 private: 48 ulong _n; 49 public: 50 this(in ulong n) { 51 _n = n; 52 } 53 54 this() { 55 this(0); 56 } 57 58 override JSONValue toJSONValue() { 59 JSONValue v; 60 v.uinteger = _n; 61 return v; 62 } 63 @property ulong number() { 64 return _n; 65 } 66 } 67 68 final class FloatValue : NumberValue { 69 private: 70 real _n; 71 public: 72 this(in real n) { 73 _n = n; 74 } 75 76 this() { 77 this(0.0f); 78 } 79 80 override JSONValue toJSONValue() { 81 JSONValue v; 82 v.floating = _n; 83 return v; 84 } 85 @property real number() { 86 return _n; 87 } 88 } 89 90 final class BoolValue : Value { 91 private: 92 bool _b; 93 public: 94 this(in bool b) { 95 _b = b; 96 } 97 98 this() { 99 this(false); 100 } 101 102 override JSONValue toJSONValue() { 103 JSONValue v; 104 v = _b; 105 return v; 106 } 107 @property bool boolean() { 108 return _b; 109 } 110 } 111 112 final class StringValue : Value { 113 private: 114 string _str; 115 116 public: 117 this(in string str) { 118 _str = str; 119 } 120 121 this() { 122 this(""); 123 } 124 125 override JSONValue toJSONValue() { 126 JSONValue v; 127 v.str = _str; 128 return v; 129 } 130 131 @property string text() { 132 return _str; 133 } 134 } 135 136 final class ObjectValue : Value { 137 private: 138 Value[string] _values; 139 140 public: 141 this(Value[string] values) { 142 _values = values; 143 } 144 145 this(JSONValue[string] values) { 146 foreach(string name, ref JSONValue v; values) { 147 this._values[name] = v.wrap(); 148 } 149 } 150 151 this() { 152 153 } 154 155 override JSONValue toJSONValue() { 156 JSONValue v; 157 JSONValue[string] props; 158 foreach(string name, Value v; _values) { 159 props[name] = v.toJSONValue(); 160 } 161 v.object = props; 162 return v; 163 } 164 165 @property Value[string] properties() { 166 return _values; 167 } 168 169 auto opBinaryRight(string op : "in")(string k) const 170 { 171 return k in _values; 172 } 173 174 Value opIndex(string k) 175 { 176 if(k !in this) { 177 return null; 178 } 179 return _values[k]; 180 } 181 182 Value opIndexAssign(Value v, string k) { 183 _values[k] = v; 184 return v; 185 } 186 } 187 188 final class ArrayValue : Value { 189 private: 190 Value[] _values; 191 192 public: 193 this(Value[] values) { 194 _values = values; 195 } 196 197 this(JSONValue[] values) { 198 foreach(ref JSONValue v; values) { 199 this._values ~= v.wrap(); 200 } 201 } 202 203 this() { 204 205 } 206 207 override JSONValue toJSONValue() { 208 JSONValue v; 209 JSONValue[] items; 210 foreach(ref Value i; _values) { 211 items ~= i.toJSONValue(); 212 } 213 v.array = items; 214 return v; 215 } 216 217 @property Value[] items() { 218 return _values; 219 } 220 221 Value opIndex(size_t i) 222 { 223 if(i > _values.length -1) { 224 return null; 225 } 226 return _values[i]; 227 } 228 Value opIndexAssign(Value v, size_t i) { 229 _values[i] = v; 230 return v; 231 } 232 } 233 234 final class NullValue : Value { 235 236 override JSONValue toJSONValue() { 237 JSONValue v = null; 238 return v; 239 } 240 } 241 242 Value wrap(JSONValue value) { 243 if(value == JSONValue.init) { 244 return null; 245 } 246 switch(value.type) { 247 case JSON_TYPE.STRING: { 248 return new StringValue(value.str); 249 } 250 case JSON_TYPE.INTEGER: { 251 return new IntegerValue(value.integer); 252 } 253 case JSON_TYPE.UINTEGER: { 254 return new UIntegerValue(value.uinteger); 255 } 256 case JSON_TYPE.FLOAT: { 257 return new FloatValue(value.floating); 258 } 259 case JSON_TYPE.OBJECT: { 260 return new ObjectValue(value.object); 261 } 262 case JSON_TYPE.ARRAY: { 263 return new ArrayValue(value.array); 264 } 265 case JSON_TYPE.NULL: { 266 return new NullValue(); 267 } 268 case JSON_TYPE.TRUE: { 269 return new BoolValue(true); 270 } 271 case JSON_TYPE.FALSE: { 272 return new BoolValue(false); 273 } 274 default: 275 throw new Exception("Unknown JSON_TYPE"); 276 } 277 } 278 279 Value parse(string doc) { 280 auto document = parseJSON(doc); 281 return document.wrap(); 282 } 283 284 Value toValue(T)(T val) { 285 static if(is(T: bool)) { 286 return new BoolValue(val); 287 } 288 else static if(is(T : long)) { 289 return new IntegerValue(val); 290 } else static if(is(T: string)) { 291 return new StringValue(val); 292 } else static if(isFloatingPoint!(T)) { 293 return new FloatValue(val); 294 } else static if(isArray!(T)) { 295 return new ArrayValue(val); 296 } else static if(is(T : Value[Key], Key)) { 297 static assert(is(Key : string), text("Key must be string but ", Key.stringof, " was given instead")); 298 return new ObjectValue(val); 299 } else { 300 static assert(false, text(`unable to convert type "`, T.stringof, `" to json Value`)); 301 } 302 } 303