1 /** 2 * JSON encoding. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.sd.json; 15 16 import std.conv; 17 import std.exception; 18 import std.format; 19 import std.string : format; 20 import std.traits; 21 import std.utf; 22 23 import ae.utils.meta; 24 import ae.utils.text; 25 26 import ae.utils.sd.sd; 27 28 /// Serialization source which parses a JSON stream. 29 struct JsonParser(C) 30 { 31 alias Char = C; 32 33 C[] s; 34 size_t p; 35 36 C next() 37 { 38 enforce(p < s.length); 39 return s[p++]; 40 } 41 42 void skip() 43 { 44 p++; 45 } 46 47 C[] readN(size_t n) 48 { 49 auto end = p + n; 50 enforce(end <= s.length); 51 C[] result = s[p .. end]; 52 p = end; 53 return result; 54 } 55 56 C peek() 57 { 58 enforce(p < s.length); 59 return s[p]; 60 } 61 62 size_t mark() 63 { 64 return p; 65 } 66 67 C[] slice(size_t a, size_t b) 68 { 69 return s[a..b]; 70 } 71 72 @property bool eof() { return p == s.length; } 73 74 // ******************************************************************* 75 76 static bool isWhite(C c) 77 { 78 return c == ' ' || c == '\t'; 79 } 80 81 void skipWhitespace() 82 { 83 while (isWhite(peek())) 84 skip(); 85 } 86 87 void expect(C c) 88 { 89 auto n = next(); 90 enforce(n==c, "Expected %s, got %s".format(c, n)); 91 } 92 93 // ******************************************************************* 94 95 void read(Sink)(Sink sink) 96 { 97 skipWhitespace(); 98 switch (peek()) 99 { 100 case '[': 101 skip(); 102 sink.handleArray(boundFunctorOf!readArray); 103 break; 104 case '"': 105 skip(); 106 sink.handleStringFragments(boundFunctorOf!readString); 107 break; 108 case 't': 109 skip(); 110 expect('r'); 111 expect('u'); 112 expect('e'); 113 sink.handleBoolean(true); 114 break; 115 case 'f': 116 skip(); 117 expect('a'); 118 expect('l'); 119 expect('s'); 120 expect('e'); 121 sink.handleBoolean(false); 122 break; 123 case 'n': 124 skip(); 125 expect('u'); 126 expect('l'); 127 expect('l'); 128 sink.handleNull(); 129 break; 130 case '-': 131 case '0': 132 .. 133 case '9': 134 sink.handleNumeric(readNumeric()); 135 break; 136 case '{': 137 skip(); 138 sink.handleObject(boundFunctorOf!readObject); 139 break; 140 default: 141 throw new Exception("Unknown JSON symbol: %s".format(peek())); 142 } 143 } 144 145 void readArray(Sink)(Sink sink) 146 { 147 if (peek()==']') 148 { 149 skip(); 150 return; 151 } 152 while (true) 153 { 154 read(sink); 155 skipWhitespace(); 156 if (peek()==']') 157 { 158 skip(); 159 return; 160 } 161 else 162 expect(','); 163 } 164 } 165 166 void readObject(Sink)(Sink sink) 167 { 168 skipWhitespace(); 169 if (peek()=='}') 170 { 171 skip(); 172 return; 173 } 174 175 while (true) 176 { 177 sink.handleField(boundFunctorOf!read, boundFunctorOf!readObjectValue); 178 179 skipWhitespace(); 180 if (peek()=='}') 181 { 182 skip(); 183 return; 184 } 185 else 186 expect(','); 187 } 188 } 189 190 void readObjectValue(Sink)(Sink sink) 191 { 192 skipWhitespace(); 193 expect(':'); 194 read(sink); 195 } 196 197 /// This will call sink.handleStringFragment multiple times. 198 void readString(Sink)(Sink sink) 199 { 200 auto start = mark(); 201 202 void flush() 203 { 204 auto end = mark(); 205 if (start != end) 206 sink.handleStringFragment(slice(start, end)); 207 } 208 209 void oneConst(C c)() 210 { 211 static C[1] arr = [c]; 212 sink.handleStringFragment(arr[]); 213 } 214 215 while (true) 216 { 217 C c = peek(); 218 if (c=='"') 219 { 220 flush(); 221 skip(); 222 return; 223 } 224 else 225 if (c=='\\') 226 { 227 flush(); 228 skip(); 229 switch (next()) 230 { 231 case '"': oneConst!('"'); break; 232 case '/': oneConst!('/'); break; 233 case '\\': oneConst!('\\'); break; 234 case 'b': oneConst!('\b'); break; 235 case 'f': oneConst!('\f'); break; 236 case 'n': oneConst!('\n'); break; 237 case 'r': oneConst!('\r'); break; 238 case 't': oneConst!('\t'); break; 239 case 'u': 240 { 241 auto w = cast(wchar)fromHex!ushort(readN(4)); 242 static if (C.sizeof == 1) 243 { 244 char[4] buf; 245 sink.handleStringFragment(buf[0..encode(buf, w)]); 246 } 247 else 248 { 249 Unqual!C[1] buf; 250 buf[0] = w; 251 sink.handleStringFragment(buf[]); 252 } 253 break; 254 } 255 default: enforce(false, "Unknown escape"); 256 } 257 start = mark(); 258 } 259 else 260 skip(); 261 } 262 } 263 264 C[] readNumeric() 265 { 266 auto p = mark(); 267 268 static immutable bool[256] numeric = 269 [ 270 '0':true, 271 '1':true, 272 '2':true, 273 '3':true, 274 '4':true, 275 '5':true, 276 '6':true, 277 '7':true, 278 '8':true, 279 '9':true, 280 '.':true, 281 '-':true, 282 '+':true, 283 'e':true, 284 'E':true, 285 ]; 286 287 while (!eof() && numeric[peek()]) 288 skip(); 289 return slice(p, mark()); 290 } 291 } 292 293 // /// Encapsulates a reference to some field of a type. 294 // struct FieldRef( 295 // /// The type holding the field 296 // T, 297 // /// Field resolver - should return a pointer to the field given a T* 298 // alias resolve, 299 // ) 300 // { 301 // T* _FieldRef_ptr; 302 // ref T _FieldRef_ref() { return *resolve(_FieldRef_ptr); } 303 // alias _FieldRef_ref this; 304 // } 305 306 struct JsonDeserializer(C) 307 { 308 JsonParser!C.Data jsonData; 309 alias DataRef = FieldRef!(typeof(jsonData), p => p); 310 void[0] anchor; 311 alias Deserializer!anchor deserializer; 312 313 this(C[] s) 314 { 315 jsonData.s = s; 316 } 317 318 T deserialize(T)() 319 { 320 T t; 321 auto sink = deserializer.makeSink(&t); 322 alias JsonImpl = JsonParser!C.Impl!DataRef; 323 auto jsonImpl = JsonImpl(DataRef(&jsonData)); 324 jsonImpl.read(sink); 325 return t; 326 } 327 } 328 329 /// Parse JSON from a string and deserialize it into the given type. 330 T jsonParse(T, C)(C[] s) 331 { 332 auto parser = JsonDeserializer!C(s); 333 // mixin(exceptionContext(q{format("Error at position %d", parser.p)})); 334 return parser.deserialize!T(); 335 } 336 337 // *************************************************************************** 338 339 struct Escapes 340 { 341 string[256] chars; 342 bool[256] escaped; 343 344 void populate() 345 { 346 import std.string; 347 348 escaped[] = true; 349 foreach (c; 0..256) 350 if (c=='\\') 351 chars[c] = `\\`; 352 else 353 if (c=='\"') 354 chars[c] = `\"`; 355 else 356 if (c=='\b') 357 chars[c] = `\b`; 358 else 359 if (c=='\f') 360 chars[c] = `\f`; 361 else 362 if (c=='\n') 363 chars[c] = `\n`; 364 else 365 if (c=='\r') 366 chars[c] = `\r`; 367 else 368 if (c=='\t') 369 chars[c] = `\t`; 370 else 371 if (c<'\x20' || c == '\x7F' || c=='<' || c=='>' || c=='&') 372 chars[c] = format(`\u%04x`, c); 373 else 374 chars[c] = [cast(char)c], 375 escaped[c] = false; 376 } 377 } 378 immutable Escapes escapes = { Escapes escapes; escapes.populate(); return escapes; }(); 379 380 /// Serialization target which writes a JSON stream. 381 struct JsonWriter 382 { 383 static template Impl(alias source, alias output) 384 { 385 alias Parent = RefType!(thisOf!source); 386 387 static template Sink(alias output) 388 { 389 void handleNumeric(C)(C[] str) 390 { 391 output.put(str); 392 } 393 394 void handleString(C)(C[] str) 395 { 396 output.put('"'); 397 handleStringFragment(str); 398 output.put('"'); 399 } 400 401 void handleNull() 402 { 403 output.put("null"); 404 } 405 406 void handleBoolean(bool v) 407 { 408 output.put(v ? "true" : "false"); 409 } 410 411 void handleStringFragment(C)(C[] s) 412 { 413 auto start = s.ptr, p = start, end = start+s.length; 414 415 while (p < end) 416 { 417 auto c = *p++; 418 if (escapes.escaped[c]) 419 output.put(start[0..p-start-1], escapes.chars[c]), 420 start = p; 421 } 422 423 output.put(start[0..p-start]); 424 } 425 426 void handleArray(Reader)(Reader reader) 427 { 428 needComma = false; 429 output.put('['); 430 reader(scopeProxy!arraySink); 431 output.put(']'); 432 } 433 434 void handleStringFragments(Reader)(Reader reader) 435 { 436 output.put('"'); 437 reader(scopeProxy!sink); 438 output.put('"'); 439 } 440 441 void handleObject(Reader)(Reader reader) 442 { 443 needComma = false; 444 output.put('{'); 445 reader(scopeProxy!objectSink); 446 output.put('}'); 447 } 448 } 449 alias sink = Sink!output; 450 451 static bool needComma; // Yes, a global 452 453 static template ArraySink(alias output) 454 { 455 alias handleObject = opDispatch!"handleObject"; 456 alias handleStringFragments = opDispatch!"handleStringFragments"; 457 alias handleStringFragment = opDispatch!"handleStringFragment"; 458 alias handleBoolean = opDispatch!"handleBoolean"; 459 alias handleNull = opDispatch!"handleNull"; 460 alias handleNumeric = opDispatch!"handleNumeric"; 461 alias handleString = opDispatch!"handleString"; 462 alias handleArray = opDispatch!"handleArray"; 463 464 template opDispatch(string name) 465 { 466 void opDispatch(Args...)(auto ref Args args) 467 { 468 if (needComma) 469 { 470 output.put(','); 471 needComma = false; 472 } 473 474 mixin("Sink!output." ~ name ~ "(args);"); 475 needComma = true; 476 } 477 } 478 } 479 alias arraySink = ArraySink!output; 480 481 static template ObjectSink(alias output) 482 { 483 void handleField(NameReader, ValueReader)(NameReader nameReader, ValueReader valueReader) 484 { 485 if (needComma) 486 { 487 output.put(','); 488 needComma = false; 489 } 490 491 nameReader (scopeProxy!sink); 492 output.put(':'); 493 valueReader(scopeProxy!sink); 494 495 needComma = true; 496 } 497 } 498 alias objectSink = ObjectSink!output; 499 500 auto makeSink() 501 { 502 auto s = scopeProxy!sink; 503 return s; 504 } 505 } 506 } 507 508 struct JsonSerializer(C) 509 { 510 static assert(is(C == char), "TODO"); 511 import ae.utils.textout; 512 513 void[0] anchor; 514 alias Serializer.Impl!anchor serializer; 515 516 StringBuilder sb; 517 alias JsonWriter.Impl!(serializer, sb) writer; 518 519 void serialize(T)(auto ref T v) 520 { 521 auto sink = writer.makeSink(); 522 serializer.read(sink, v); 523 } 524 } 525 526 S toJson(S = string, T)(auto ref T v) 527 { 528 JsonSerializer!(Unqual!(typeof(S.init[0]))) s; 529 s.serialize(v); 530 return s.sb.get(); 531 } 532 533 // *************************************************************************** 534