Newer
Older
dub_jkp / source / dub / internal / vibecompat / data / json.d
  1. /**
  2. JSON serialization and value handling.
  3.  
  4. This module provides the Json struct for reading, writing and manipulating
  5. JSON values. De(serialization) of arbitrary D types is also supported and
  6. is recommended for handling JSON in performance sensitive applications.
  7.  
  8. Copyright: © 2012-2015 RejectedSoftware e.K.
  9. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  10. Authors: Sönke Ludwig
  11. */
  12. module dub.internal.vibecompat.data.json;
  13.  
  14. version (Have_vibe_d) public import vibe.data.json;
  15. else:
  16.  
  17. import dub.internal.vibecompat.data.utils;
  18.  
  19. public import dub.internal.vibecompat.data.serialization;
  20.  
  21. public import std.json : JSONException;
  22. import std.algorithm : equal, min;
  23. import std.array;
  24. import std.conv;
  25. import std.datetime;
  26. import std.exception;
  27. import std.format;
  28. import std.range;
  29. import std.string : format;
  30. import std.traits;
  31.  
  32. version = JsonLineNumbers;
  33. version = VibeJsonFieldNames;
  34.  
  35.  
  36. /******************************************************************************/
  37. /* public types */
  38. /******************************************************************************/
  39.  
  40. /**
  41. Represents a single JSON value.
  42.  
  43. Json values can have one of the types defined in the Json.Type enum. They
  44. behave mostly like values in ECMA script in the way that you can
  45. transparently perform operations on them. However, strict typechecking is
  46. done, so that operations between differently typed JSON values will throw
  47. a JSONException. Additionally, an explicit cast or using get!() or to!() is
  48. required to convert a JSON value to the corresponding static D type.
  49. */
  50. struct Json {
  51. private {
  52. // putting all fields in a union results in many false pointers leading to
  53. // memory leaks and, worse, std.algorithm.swap triggering an assertion
  54. // because of internal pointers. This crude workaround seems to fix
  55. // the issues.
  56. void*[2] m_data;
  57. ref inout(T) getDataAs(T)() inout { static assert(T.sizeof <= m_data.sizeof); return *cast(inout(T)*)m_data.ptr; }
  58. @property ref inout(long) m_int() inout { return getDataAs!long(); }
  59. @property ref inout(double) m_float() inout { return getDataAs!double(); }
  60. @property ref inout(bool) m_bool() inout { return getDataAs!bool(); }
  61. @property ref inout(string) m_string() inout { return getDataAs!string(); }
  62. @property ref inout(Json[string]) m_object() inout { return getDataAs!(Json[string])(); }
  63. @property ref inout(Json[]) m_array() inout { return getDataAs!(Json[])(); }
  64.  
  65. Type m_type = Type.undefined;
  66.  
  67. version (VibeJsonFieldNames) {
  68. uint m_magic = 0x1337f00d; // works around Appender bug (DMD BUG 10690/10859/11357)
  69. string m_name;
  70. string m_fileName;
  71. }
  72. }
  73.  
  74. /** Represents the run time type of a JSON value.
  75. */
  76. enum Type {
  77. undefined, /// A non-existent value in a JSON object
  78. null_, /// Null value
  79. bool_, /// Boolean value
  80. int_, /// 64-bit integer value
  81. float_, /// 64-bit floating point value
  82. string, /// UTF-8 string
  83. array, /// Array of JSON values
  84. object, /// JSON object aka. dictionary from string to Json
  85.  
  86. Undefined = undefined, /// Compatibility alias - will be deprecated soon
  87. Null = null_, /// Compatibility alias - will be deprecated soon
  88. Bool = bool_, /// Compatibility alias - will be deprecated soon
  89. Int = int_, /// Compatibility alias - will be deprecated soon
  90. Float = float_, /// Compatibility alias - will be deprecated soon
  91. String = string, /// Compatibility alias - will be deprecated soon
  92. Array = array, /// Compatibility alias - will be deprecated soon
  93. Object = object /// Compatibility alias - will be deprecated soon
  94. }
  95.  
  96. /// New JSON value of Type.Undefined
  97. static @property Json undefined() { return Json(); }
  98.  
  99. /// New JSON value of Type.Object
  100. static @property Json emptyObject() { return Json(cast(Json[string])null); }
  101.  
  102. /// New JSON value of Type.Array
  103. static @property Json emptyArray() { return Json(cast(Json[])null); }
  104.  
  105. version(JsonLineNumbers) int line;
  106.  
  107. /**
  108. Constructor for a JSON object.
  109. */
  110. this(typeof(null)) { m_type = Type.null_; }
  111. /// ditto
  112. this(bool v) { m_type = Type.bool_; m_bool = v; }
  113. /// ditto
  114. this(byte v) { this(cast(long)v); }
  115. /// ditto
  116. this(ubyte v) { this(cast(long)v); }
  117. /// ditto
  118. this(short v) { this(cast(long)v); }
  119. /// ditto
  120. this(ushort v) { this(cast(long)v); }
  121. /// ditto
  122. this(int v) { this(cast(long)v); }
  123. /// ditto
  124. this(uint v) { this(cast(long)v); }
  125. /// ditto
  126. this(long v) { m_type = Type.int_; m_int = v; }
  127. /// ditto
  128. this(double v) { m_type = Type.float_; m_float = v; }
  129. /// ditto
  130. this(string v) { m_type = Type.string; m_string = v; }
  131. /// ditto
  132. this(Json[] v) { m_type = Type.array; m_array = v; }
  133. /// ditto
  134. this(Json[string] v) { m_type = Type.object; m_object = v; }
  135.  
  136. /**
  137. Allows assignment of D values to a JSON value.
  138. */
  139. ref Json opAssign(Json v)
  140. {
  141. m_type = v.m_type;
  142. final switch(m_type){
  143. case Type.undefined: m_string = null; break;
  144. case Type.null_: m_string = null; break;
  145. case Type.bool_: m_bool = v.m_bool; break;
  146. case Type.int_: m_int = v.m_int; break;
  147. case Type.float_: m_float = v.m_float; break;
  148. case Type.string: m_string = v.m_string; break;
  149. case Type.array: opAssign(v.m_array); break;
  150. case Type.object: opAssign(v.m_object); break;
  151. }
  152. return this;
  153. }
  154. /// ditto
  155. void opAssign(typeof(null)) { m_type = Type.null_; m_string = null; }
  156. /// ditto
  157. bool opAssign(bool v) { m_type = Type.bool_; m_bool = v; return v; }
  158. /// ditto
  159. int opAssign(int v) { m_type = Type.int_; m_int = v; return v; }
  160. /// ditto
  161. long opAssign(long v) { m_type = Type.int_; m_int = v; return v; }
  162. /// ditto
  163. double opAssign(double v) { m_type = Type.float_; m_float = v; return v; }
  164. /// ditto
  165. string opAssign(string v) { m_type = Type.string; m_string = v; return v; }
  166. /// ditto
  167. Json[] opAssign(Json[] v)
  168. {
  169. m_type = Type.array;
  170. m_array = v;
  171. version (VibeJsonFieldNames) { if (m_magic == 0x1337f00d) { foreach (idx, ref av; m_array) av.m_name = format("%s[%s]", m_name, idx); } else m_name = null; }
  172. return v;
  173. }
  174. /// ditto
  175. Json[string] opAssign(Json[string] v)
  176. {
  177. m_type = Type.object;
  178. m_object = v;
  179. version (VibeJsonFieldNames) { if (m_magic == 0x1337f00d) { foreach (key, ref av; m_object) av.m_name = format("%s.%s", m_name, key); } else m_name = null; }
  180. return v;
  181. }
  182.  
  183. /**
  184. Allows removal of values from Type.Object Json objects.
  185. */
  186. void remove(string item) { checkType!(Json[string])(); m_object.remove(item); }
  187.  
  188. /**
  189. The current type id of this JSON object.
  190. */
  191. @property Type type() const { return m_type; }
  192.  
  193. /**
  194. Clones a JSON value recursively.
  195. */
  196. Json clone()
  197. const {
  198. final switch (m_type) {
  199. case Type.undefined: return Json.undefined;
  200. case Type.null_: return Json(null);
  201. case Type.bool_: return Json(m_bool);
  202. case Type.int_: return Json(m_int);
  203. case Type.float_: return Json(m_float);
  204. case Type.string: return Json(m_string);
  205. case Type.array:
  206. auto ret = Json.emptyArray;
  207. foreach (v; this) ret ~= v.clone();
  208. return ret;
  209. case Type.object:
  210. auto ret = Json.emptyObject;
  211. foreach (string name, v; this) ret[name] = v.clone();
  212. return ret;
  213. }
  214. }
  215.  
  216. /**
  217. Check whether the JSON object contains the given key and if yes,
  218. return a pointer to the corresponding object, otherwise return `null`.
  219. */
  220. inout(Json*) opBinaryRight(string op : "in")(string key) inout {
  221. checkType!(Json[string])();
  222. return key in m_object;
  223. }
  224.  
  225. /**
  226. Allows direct indexing of array typed JSON values.
  227. */
  228. ref inout(Json) opIndex(size_t idx) inout { checkType!(Json[])(); return m_array[idx]; }
  229.  
  230. ///
  231. unittest {
  232. Json value = Json.emptyArray;
  233. value ~= 1;
  234. value ~= true;
  235. value ~= "foo";
  236. assert(value[0] == 1);
  237. assert(value[1] == true);
  238. assert(value[2] == "foo");
  239. }
  240.  
  241.  
  242. /**
  243. Allows direct indexing of object typed JSON values using a string as
  244. the key.
  245. */
  246. const(Json) opIndex(string key)
  247. const {
  248. checkType!(Json[string])();
  249. if( auto pv = key in m_object ) return *pv;
  250. Json ret = Json.undefined;
  251. ret.m_string = key;
  252. version (VibeJsonFieldNames) ret.m_name = format("%s.%s", m_name, key);
  253. return ret;
  254. }
  255. /// ditto
  256. ref Json opIndex(string key)
  257. {
  258. checkType!(Json[string])();
  259. if( auto pv = key in m_object )
  260. return *pv;
  261. if (m_object is null) {
  262. m_object = ["": Json.init];
  263. m_object.remove("");
  264. }
  265. m_object[key] = Json.init;
  266. assert(m_object !is null);
  267. assert(key in m_object, "Failed to insert key '"~key~"' into AA!?");
  268. m_object[key].m_type = Type.undefined; // DMDBUG: AAs are teh $H1T!!!11
  269. assert(m_object[key].type == Type.undefined);
  270. m_object[key].m_string = key;
  271. version (VibeJsonFieldNames) m_object[key].m_name = format("%s.%s", m_name, key);
  272. return m_object[key];
  273. }
  274.  
  275. ///
  276. unittest {
  277. Json value = Json.emptyObject;
  278. value["a"] = 1;
  279. value["b"] = true;
  280. value["c"] = "foo";
  281. assert(value["a"] == 1);
  282. assert(value["b"] == true);
  283. assert(value["c"] == "foo");
  284. }
  285.  
  286. /**
  287. Returns a slice of a JSON array.
  288. */
  289. inout(Json[]) opSlice() inout { checkType!(Json[])(); return m_array; }
  290. ///
  291. inout(Json[]) opSlice(size_t from, size_t to) inout { checkType!(Json[])(); return m_array[from .. to]; }
  292.  
  293. /**
  294. Returns the number of entries of string, array or object typed JSON values.
  295. */
  296. @property size_t length()
  297. const {
  298. checkType!(string, Json[], Json[string])("property length");
  299. switch(m_type){
  300. case Type.string: return m_string.length;
  301. case Type.array: return m_array.length;
  302. case Type.object: return m_object.length;
  303. default: assert(false);
  304. }
  305. }
  306.  
  307. /**
  308. Allows foreach iterating over JSON objects and arrays.
  309. */
  310. int opApply(int delegate(ref Json obj) del)
  311. {
  312. checkType!(Json[], Json[string])("opApply");
  313. if( m_type == Type.array ){
  314. foreach( ref v; m_array )
  315. if( auto ret = del(v) )
  316. return ret;
  317. return 0;
  318. } else {
  319. foreach( ref v; m_object )
  320. if( v.type != Type.undefined )
  321. if( auto ret = del(v) )
  322. return ret;
  323. return 0;
  324. }
  325. }
  326. /// ditto
  327. int opApply(int delegate(ref const Json obj) del)
  328. const {
  329. checkType!(Json[], Json[string])("opApply");
  330. if( m_type == Type.array ){
  331. foreach( ref v; m_array )
  332. if( auto ret = del(v) )
  333. return ret;
  334. return 0;
  335. } else {
  336. foreach( ref v; m_object )
  337. if( v.type != Type.undefined )
  338. if( auto ret = del(v) )
  339. return ret;
  340. return 0;
  341. }
  342. }
  343. /// ditto
  344. int opApply(int delegate(ref size_t idx, ref Json obj) del)
  345. {
  346. checkType!(Json[])("opApply");
  347. foreach( idx, ref v; m_array )
  348. if( auto ret = del(idx, v) )
  349. return ret;
  350. return 0;
  351. }
  352. /// ditto
  353. int opApply(int delegate(ref size_t idx, ref const Json obj) del)
  354. const {
  355. checkType!(Json[])("opApply");
  356. foreach( idx, ref v; m_array )
  357. if( auto ret = del(idx, v) )
  358. return ret;
  359. return 0;
  360. }
  361. /// ditto
  362. int opApply(int delegate(ref string idx, ref Json obj) del)
  363. {
  364. checkType!(Json[string])("opApply");
  365. foreach( idx, ref v; m_object )
  366. if( v.type != Type.undefined )
  367. if( auto ret = del(idx, v) )
  368. return ret;
  369. return 0;
  370. }
  371. /// ditto
  372. int opApply(int delegate(ref string idx, ref const Json obj) del)
  373. const {
  374. checkType!(Json[string])("opApply");
  375. foreach( idx, ref v; m_object )
  376. if( v.type != Type.undefined )
  377. if( auto ret = del(idx, v) )
  378. return ret;
  379. return 0;
  380. }
  381.  
  382. /**
  383. Converts the JSON value to the corresponding D type - types must match exactly.
  384.  
  385. Available_Types:
  386. $(UL
  387. $(LI `bool` (`Type.bool_`))
  388. $(LI `double` (`Type.float_`))
  389. $(LI `float` (Converted from `double`))
  390. $(LI `long` (`Type.int_`))
  391. $(LI `ulong`, `int`, `uint`, `short`, `ushort`, `byte`, `ubyte` (Converted from `long`))
  392. $(LI `string` (`Type.string`))
  393. $(LI `Json[]` (`Type.array`))
  394. $(LI `Json[string]` (`Type.object`))
  395. )
  396.  
  397. See_Also: `opt`, `to`, `deserializeJson`
  398. */
  399. inout(T) opCast(T)() inout { return get!T; }
  400. /// ditto
  401. @property inout(T) get(T)()
  402. inout {
  403. checkType!T();
  404. static if (is(T == bool)) return m_bool;
  405. else static if (is(T == double)) return m_float;
  406. else static if (is(T == float)) return cast(T)m_float;
  407. else static if (is(T == long)) return m_int;
  408. else static if (is(T == ulong)) return cast(ulong)m_int;
  409. else static if (is(T : long)){ enforceJson(m_int <= T.max && m_int >= T.min, "Integer conversion out of bounds error", m_fileName, line); return cast(T)m_int; }
  410. else static if (is(T == string)) return m_string;
  411. else static if (is(T == Json[])) return m_array;
  412. else static if (is(T == Json[string])) return m_object;
  413. else static assert("JSON can only be cast to (bool, long, double, string, Json[] or Json[string]. Not "~T.stringof~".");
  414. }
  415.  
  416. /**
  417. Returns the native type for this JSON if it matches the current runtime type.
  418.  
  419. If the runtime type does not match the given native type, the 'def' parameter is returned
  420. instead.
  421.  
  422. See_Also: `get`
  423. */
  424. @property const(T) opt(T)(const(T) def = T.init)
  425. const {
  426. if( typeId!T != m_type ) return def;
  427. return get!T;
  428. }
  429. /// ditto
  430. @property T opt(T)(T def = T.init)
  431. {
  432. if( typeId!T != m_type ) return def;
  433. return get!T;
  434. }
  435.  
  436. /**
  437. Converts the JSON value to the corresponding D type - types are converted as necessary.
  438.  
  439. Automatically performs conversions between strings and numbers. See
  440. `get` for the list of available types. For converting/deserializing
  441. JSON to complex data types see `deserializeJson`.
  442.  
  443. See_Also: `get`, `deserializeJson`
  444. */
  445. @property inout(T) to(T)()
  446. inout {
  447. static if( is(T == bool) ){
  448. final switch( m_type ){
  449. case Type.undefined: return false;
  450. case Type.null_: return false;
  451. case Type.bool_: return m_bool;
  452. case Type.int_: return m_int != 0;
  453. case Type.float_: return m_float != 0;
  454. case Type.string: return m_string.length > 0;
  455. case Type.array: return m_array.length > 0;
  456. case Type.object: return m_object.length > 0;
  457. }
  458. } else static if( is(T == double) ){
  459. final switch( m_type ){
  460. case Type.undefined: return T.init;
  461. case Type.null_: return 0;
  462. case Type.bool_: return m_bool ? 1 : 0;
  463. case Type.int_: return m_int;
  464. case Type.float_: return m_float;
  465. case Type.string: return .to!double(cast(string)m_string);
  466. case Type.array: return double.init;
  467. case Type.object: return double.init;
  468. }
  469. } else static if( is(T == float) ){
  470. final switch( m_type ){
  471. case Type.undefined: return T.init;
  472. case Type.null_: return 0;
  473. case Type.bool_: return m_bool ? 1 : 0;
  474. case Type.int_: return m_int;
  475. case Type.float_: return m_float;
  476. case Type.string: return .to!float(cast(string)m_string);
  477. case Type.array: return float.init;
  478. case Type.object: return float.init;
  479. }
  480. }
  481. else static if( is(T == long) ){
  482. final switch( m_type ){
  483. case Type.undefined: return 0;
  484. case Type.null_: return 0;
  485. case Type.bool_: return m_bool ? 1 : 0;
  486. case Type.int_: return m_int;
  487. case Type.float_: return cast(long)m_float;
  488. case Type.string: return .to!long(m_string);
  489. case Type.array: return 0;
  490. case Type.object: return 0;
  491. }
  492. } else static if( is(T : long) ){
  493. final switch( m_type ){
  494. case Type.undefined: return 0;
  495. case Type.null_: return 0;
  496. case Type.bool_: return m_bool ? 1 : 0;
  497. case Type.int_: return cast(T)m_int;
  498. case Type.float_: return cast(T)m_float;
  499. case Type.string: return cast(T).to!long(cast(string)m_string);
  500. case Type.array: return 0;
  501. case Type.object: return 0;
  502. }
  503. } else static if( is(T == string) ){
  504. switch( m_type ){
  505. default: return toString();
  506. case Type.string: return m_string;
  507. }
  508. } else static if( is(T == Json[]) ){
  509. switch( m_type ){
  510. default: return Json([this]);
  511. case Type.array: return m_array;
  512. }
  513. } else static if( is(T == Json[string]) ){
  514. switch( m_type ){
  515. default: return Json(["value": this]);
  516. case Type.object: return m_object;
  517. }
  518. } else static assert("JSON can only be cast to (bool, long, double, string, Json[] or Json[string]. Not "~T.stringof~".");
  519. }
  520.  
  521. /**
  522. Performs unary operations on the JSON value.
  523.  
  524. The following operations are supported for each type:
  525.  
  526. $(DL
  527. $(DT Null) $(DD none)
  528. $(DT Bool) $(DD ~)
  529. $(DT Int) $(DD +, -, ++, --)
  530. $(DT Float) $(DD +, -, ++, --)
  531. $(DT String) $(DD none)
  532. $(DT Array) $(DD none)
  533. $(DT Object) $(DD none)
  534. )
  535. */
  536. Json opUnary(string op)()
  537. const {
  538. static if( op == "~" ){
  539. checkType!bool();
  540. return Json(~m_bool);
  541. } else static if( op == "+" || op == "-" || op == "++" || op == "--" ){
  542. checkType!(long, double)("unary "~op);
  543. if( m_type == Type.int_ ) mixin("return Json("~op~"m_int);");
  544. else if( m_type == Type.float_ ) mixin("return Json("~op~"m_float);");
  545. else assert(false);
  546. } else static assert("Unsupported operator '"~op~"' for type JSON.");
  547. }
  548.  
  549. /**
  550. Performs binary operations between JSON values.
  551.  
  552. The two JSON values must be of the same run time type or a JSONException
  553. will be thrown. Only the operations listed are allowed for each of the
  554. types.
  555.  
  556. $(DL
  557. $(DT Null) $(DD none)
  558. $(DT Bool) $(DD &&, ||)
  559. $(DT Int) $(DD +, -, *, /, %)
  560. $(DT Float) $(DD +, -, *, /, %)
  561. $(DT String) $(DD ~)
  562. $(DT Array) $(DD ~)
  563. $(DT Object) $(DD in)
  564. )
  565. */
  566. Json opBinary(string op)(ref const(Json) other)
  567. const {
  568. enforceJson(m_type == other.m_type, "Binary operation '"~op~"' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects.");
  569. static if( op == "&&" ){
  570. checkType!(bool)(op);
  571. return Json(m_bool && other.m_bool);
  572. } else static if( op == "||" ){
  573. checkType!(bool)(op);
  574. return Json(m_bool || other.m_bool);
  575. } else static if( op == "+" ){
  576. checkType!(long, double)(op);
  577. if( m_type == Type.Int ) return Json(m_int + other.m_int);
  578. else if( m_type == Type.float_ ) return Json(m_float + other.m_float);
  579. else assert(false);
  580. } else static if( op == "-" ){
  581. checkType!(long, double)(op);
  582. if( m_type == Type.Int ) return Json(m_int - other.m_int);
  583. else if( m_type == Type.float_ ) return Json(m_float - other.m_float);
  584. else assert(false);
  585. } else static if( op == "*" ){
  586. checkType!(long, double)(op);
  587. if( m_type == Type.Int ) return Json(m_int * other.m_int);
  588. else if( m_type == Type.float_ ) return Json(m_float * other.m_float);
  589. else assert(false);
  590. } else static if( op == "/" ){
  591. checkType!(long, double)(op);
  592. if( m_type == Type.Int ) return Json(m_int / other.m_int);
  593. else if( m_type == Type.float_ ) return Json(m_float / other.m_float);
  594. else assert(false);
  595. } else static if( op == "%" ){
  596. checkType!(long, double)(op);
  597. if( m_type == Type.Int ) return Json(m_int % other.m_int);
  598. else if( m_type == Type.float_ ) return Json(m_float % other.m_float);
  599. else assert(false);
  600. } else static if( op == "~" ){
  601. checkType!(string, Json[])(op);
  602. if( m_type == Type.string ) return Json(m_string ~ other.m_string);
  603. else if (m_type == Type.array) return Json(m_array ~ other.m_array);
  604. else assert(false);
  605. } else static assert("Unsupported operator '"~op~"' for type JSON.");
  606. }
  607. /// ditto
  608. Json opBinary(string op)(Json other)
  609. if( op == "~" )
  610. {
  611. static if( op == "~" ){
  612. checkType!(string, Json[])(op);
  613. if( m_type == Type.string ) return Json(m_string ~ other.m_string);
  614. else if( m_type == Type.array ) return Json(m_array ~ other.m_array);
  615. else assert(false);
  616. } else static assert("Unsupported operator '"~op~"' for type JSON.");
  617. }
  618. /// ditto
  619. void opOpAssign(string op)(Json other)
  620. if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op =="~")
  621. {
  622. enforceJson(m_type == other.m_type || op == "~" && m_type == Type.array,
  623. "Binary operation '"~op~"=' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects.");
  624. static if( op == "+" ){
  625. if( m_type == Type.int_ ) m_int += other.m_int;
  626. else if( m_type == Type.float_ ) m_float += other.m_float;
  627. else enforceJson(false, "'+=' only allowed for scalar types, not "~.to!string(m_type)~".");
  628. } else static if( op == "-" ){
  629. if( m_type == Type.int_ ) m_int -= other.m_int;
  630. else if( m_type == Type.float_ ) m_float -= other.m_float;
  631. else enforceJson(false, "'-=' only allowed for scalar types, not "~.to!string(m_type)~".");
  632. } else static if( op == "*" ){
  633. if( m_type == Type.int_ ) m_int *= other.m_int;
  634. else if( m_type == Type.float_ ) m_float *= other.m_float;
  635. else enforceJson(false, "'*=' only allowed for scalar types, not "~.to!string(m_type)~".");
  636. } else static if( op == "/" ){
  637. if( m_type == Type.int_ ) m_int /= other.m_int;
  638. else if( m_type == Type.float_ ) m_float /= other.m_float;
  639. else enforceJson(false, "'/=' only allowed for scalar types, not "~.to!string(m_type)~".");
  640. } else static if( op == "%" ){
  641. if( m_type == Type.int_ ) m_int %= other.m_int;
  642. else if( m_type == Type.float_ ) m_float %= other.m_float;
  643. else enforceJson(false, "'%=' only allowed for scalar types, not "~.to!string(m_type)~".");
  644. } else static if( op == "~" ){
  645. if (m_type == Type.string) m_string ~= other.m_string;
  646. else if (m_type == Type.array) {
  647. if (other.m_type == Type.array) m_array ~= other.m_array;
  648. else appendArrayElement(other);
  649. } else enforceJson(false, "'~=' only allowed for string and array types, not "~.to!string(m_type)~".");
  650. } else static assert("Unsupported operator '"~op~"=' for type JSON.");
  651. }
  652. /// ditto
  653. void opOpAssign(string op, T)(T other)
  654. if (!is(T == Json) && is(typeof(Json(other))))
  655. {
  656. opOpAssign!op(Json(other));
  657. }
  658. /// ditto
  659. Json opBinary(string op)(bool other) const { checkType!bool(); mixin("return Json(m_bool "~op~" other);"); }
  660. /// ditto
  661. Json opBinary(string op)(long other) const { checkType!long(); mixin("return Json(m_int "~op~" other);"); }
  662. /// ditto
  663. Json opBinary(string op)(double other) const { checkType!double(); mixin("return Json(m_float "~op~" other);"); }
  664. /// ditto
  665. Json opBinary(string op)(string other) const { checkType!string(); mixin("return Json(m_string "~op~" other);"); }
  666. /// ditto
  667. Json opBinary(string op)(Json[] other) { checkType!(Json[])(); mixin("return Json(m_array "~op~" other);"); }
  668. /// ditto
  669. Json opBinaryRight(string op)(bool other) const { checkType!bool(); mixin("return Json(other "~op~" m_bool);"); }
  670. /// ditto
  671. Json opBinaryRight(string op)(long other) const { checkType!long(); mixin("return Json(other "~op~" m_int);"); }
  672. /// ditto
  673. Json opBinaryRight(string op)(double other) const { checkType!double(); mixin("return Json(other "~op~" m_float);"); }
  674. /// ditto
  675. Json opBinaryRight(string op)(string other) const if(op == "~") { checkType!string(); return Json(other ~ m_string); }
  676. /// ditto
  677. inout(Json)* opBinaryRight(string op)(string other) inout if(op == "in") {
  678. checkType!(Json[string])();
  679. auto pv = other in m_object;
  680. if( !pv ) return null;
  681. if( pv.type == Type.undefined ) return null;
  682. return pv;
  683. }
  684. /// ditto
  685. Json opBinaryRight(string op)(Json[] other) { checkType!(Json[])(); mixin("return Json(other "~op~" m_array);"); }
  686.  
  687. /**
  688. * The append operator will append arrays. This method always appends it's argument as an array element, so nested arrays can be created.
  689. */
  690. void appendArrayElement(Json element)
  691. {
  692. enforceJson(m_type == Type.array, "'appendArrayElement' only allowed for array types, not "~.to!string(m_type)~".");
  693. m_array ~= element;
  694. }
  695.  
  696. /**
  697. Compares two JSON values for equality.
  698.  
  699. If the two values have different types, they are considered unequal.
  700. This differs with ECMA script, which performs a type conversion before
  701. comparing the values.
  702. */
  703. bool opEquals(ref const Json other)
  704. const {
  705. if( m_type != other.m_type ) return false;
  706. final switch(m_type){
  707. case Type.undefined: return false;
  708. case Type.null_: return true;
  709. case Type.bool_: return m_bool == other.m_bool;
  710. case Type.int_: return m_int == other.m_int;
  711. case Type.float_: return m_float == other.m_float;
  712. case Type.string: return m_string == other.m_string;
  713. case Type.array: return m_array == other.m_array;
  714. case Type.object: return m_object == other.m_object;
  715. }
  716. }
  717. /// ditto
  718. bool opEquals(const Json other) const { return opEquals(other); }
  719. /// ditto
  720. bool opEquals(typeof(null)) const { return m_type == Type.null_; }
  721. /// ditto
  722. bool opEquals(bool v) const { return m_type == Type.bool_ && m_bool == v; }
  723. /// ditto
  724. bool opEquals(int v) const { return m_type == Type.int_ && m_int == v; }
  725. /// ditto
  726. bool opEquals(long v) const { return m_type == Type.int_ && m_int == v; }
  727. /// ditto
  728. bool opEquals(double v) const { return m_type == Type.float_ && m_float == v; }
  729. /// ditto
  730. bool opEquals(string v) const { return m_type == Type.string && m_string == v; }
  731.  
  732. /**
  733. Compares two JSON values.
  734.  
  735. If the types of the two values differ, the value with the smaller type
  736. id is considered the smaller value. This differs from ECMA script, which
  737. performs a type conversion before comparing the values.
  738.  
  739. JSON values of type Object cannot be compared and will throw an
  740. exception.
  741. */
  742. int opCmp(ref const Json other)
  743. const {
  744. if( m_type != other.m_type ) return m_type < other.m_type ? -1 : 1;
  745. final switch(m_type){
  746. case Type.undefined: return 0;
  747. case Type.null_: return 0;
  748. case Type.bool_: return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1;
  749. case Type.int_: return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1;
  750. case Type.float_: return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1;
  751. case Type.string: return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1;
  752. case Type.array: return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1;
  753. case Type.object:
  754. enforceJson(false, "JSON objects cannot be compared.");
  755. assert(false);
  756. }
  757. }
  758.  
  759. alias opDollar = length;
  760.  
  761. /**
  762. Returns the type id corresponding to the given D type.
  763. */
  764. static @property Type typeId(T)() {
  765. static if( is(T == typeof(null)) ) return Type.null_;
  766. else static if( is(T == bool) ) return Type.bool_;
  767. else static if( is(T == double) ) return Type.float_;
  768. else static if( is(T == float) ) return Type.float_;
  769. else static if( is(T : long) ) return Type.int_;
  770. else static if( is(T == string) ) return Type.string;
  771. else static if( is(T == Json[]) ) return Type.array;
  772. else static if( is(T == Json[string]) ) return Type.object;
  773. else static assert(false, "Unsupported JSON type '"~T.stringof~"'. Only bool, long, double, string, Json[] and Json[string] are allowed.");
  774. }
  775.  
  776. /**
  777. Returns the JSON object as a string.
  778.  
  779. For large JSON values use writeJsonString instead as this function will store the whole string
  780. in memory, whereas writeJsonString writes it out bit for bit.
  781.  
  782. See_Also: writeJsonString, toPrettyString
  783. */
  784. string toString()
  785. const {
  786. auto ret = appender!string();
  787. writeJsonString(ret, this);
  788. return ret.data;
  789. }
  790.  
  791. /**
  792. Returns the JSON object as a "pretty" string.
  793.  
  794. ---
  795. auto json = Json(["foo": Json("bar")]);
  796. writeln(json.toPrettyString());
  797.  
  798. // output:
  799. // {
  800. // "foo": "bar"
  801. // }
  802. ---
  803.  
  804. Params:
  805. level = Specifies the base amount of indentation for the output. Indentation is always
  806. done using tab characters.
  807.  
  808. See_Also: writePrettyJsonString, toString
  809. */
  810. string toPrettyString(int level = 0)
  811. const {
  812. auto ret = appender!string();
  813. writePrettyJsonString(ret, this, level);
  814. return ret.data;
  815. }
  816.  
  817. private void checkType(TYPES...)(string op = null)
  818. const {
  819. bool matched = false;
  820. foreach (T; TYPES) if (m_type == typeId!T) matched = true;
  821. if (matched) return;
  822.  
  823. string name;
  824. version (VibeJsonFieldNames) {
  825. if (m_name.length) name = m_name ~ " of type " ~ m_type.to!string;
  826. else name = "JSON of type " ~ m_type.to!string;
  827. } else name = "JSON of type " ~ m_type.to!string;
  828.  
  829. string expected;
  830. static if (TYPES.length == 1) expected = typeId!(TYPES[0]).to!string;
  831. else {
  832. foreach (T; TYPES) {
  833. if (expected.length > 0) expected ~= ", ";
  834. expected ~= typeId!T.to!string;
  835. }
  836. }
  837.  
  838. enforceJson(op.length > 0, format("Got %s, expected %s.", name, expected), m_fileName, line);
  839. enforceJson(false, format("Got %s, expected %s for %s.", name, expected, op), m_fileName, line);
  840. }
  841.  
  842. /*invariant()
  843. {
  844. assert(m_type >= Type.Undefined && m_type <= Type.Object);
  845. }*/
  846. }
  847.  
  848.  
  849. /******************************************************************************/
  850. /* public functions */
  851. /******************************************************************************/
  852.  
  853. /**
  854. Parses the given range as a JSON string and returns the corresponding Json object.
  855.  
  856. The range is shrunk during parsing, leaving any remaining text that is not part of
  857. the JSON contents.
  858.  
  859. Throws a JSONException if any parsing error occurred.
  860. */
  861. Json parseJson(R)(ref R range, int* line = null, string filename = null)
  862. if( is(R == string) )
  863. {
  864. import std.string : startsWith;
  865.  
  866. Json ret;
  867. enforceJson(!range.empty, "JSON string is empty.", filename, 0);
  868.  
  869. skipWhitespace(range, line);
  870.  
  871. version(JsonLineNumbers) {
  872. import dub.internal.vibecompat.core.log;
  873. int curline = line ? *line : 0;
  874. }
  875.  
  876. switch( range.front ){
  877. case 'f':
  878. enforceJson(range[1 .. $].startsWith("alse"), "Expected 'false', got '"~range[0 .. min(5, $)]~"'.", filename, line);
  879. range.popFrontN(5);
  880. ret = false;
  881. break;
  882. case 'n':
  883. enforceJson(range[1 .. $].startsWith("ull"), "Expected 'null', got '"~range[0 .. min(4, $)]~"'.", filename, line);
  884. range.popFrontN(4);
  885. ret = null;
  886. break;
  887. case 't':
  888. enforceJson(range[1 .. $].startsWith("rue"), "Expected 'true', got '"~range[0 .. min(4, $)]~"'.", filename, line);
  889. range.popFrontN(4);
  890. ret = true;
  891. break;
  892. case '0': .. case '9':
  893. case '-':
  894. bool is_float;
  895. auto num = skipNumber(range, is_float, filename, line);
  896. if( is_float ) ret = to!double(num);
  897. else ret = to!long(num);
  898. break;
  899. case '\"':
  900. ret = skipJsonString(range, filename, line);
  901. break;
  902. case '[':
  903. Json[] arr;
  904. range.popFront();
  905. while (true) {
  906. skipWhitespace(range, line);
  907. enforceJson(!range.empty, "Missing ']' before EOF.", filename, line);
  908. if(range.front == ']') break;
  909. arr ~= parseJson(range, line, filename);
  910. skipWhitespace(range, line);
  911. enforceJson(!range.empty, "Missing ']' before EOF.", filename, line);
  912. enforceJson(range.front == ',' || range.front == ']',
  913. format("Expected ']' or ',' - got '%s'.", range.front), filename, line);
  914. if( range.front == ']' ) break;
  915. else range.popFront();
  916. }
  917. range.popFront();
  918. ret = arr;
  919. break;
  920. case '{':
  921. Json[string] obj;
  922. range.popFront();
  923. while (true) {
  924. skipWhitespace(range, line);
  925. enforceJson(!range.empty, "Missing '}' before EOF.", filename, line);
  926. if(range.front == '}') break;
  927. string key = skipJsonString(range, filename, line);
  928. skipWhitespace(range, line);
  929. enforceJson(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, line);
  930. range.popFront();
  931. skipWhitespace(range, line);
  932. Json itm = parseJson(range, line, filename);
  933. obj[key] = itm;
  934. skipWhitespace(range, line);
  935. enforceJson(!range.empty, "Missing '}' before EOF.", filename, line);
  936. enforceJson(range.front == ',' || range.front == '}',
  937. format("Expected '}' or ',' - got '%s'.", range.front), filename, line);
  938. if (range.front == '}') break;
  939. else range.popFront();
  940. }
  941. range.popFront();
  942. ret = obj;
  943. break;
  944. default:
  945. enforceJson(false, format("Expected valid JSON token, got '%s'.", range[0 .. min(12, $)]), filename, line);
  946. assert(false);
  947. }
  948.  
  949. assert(ret.type != Json.Type.undefined);
  950. version(JsonLineNumbers) ret.line = curline;
  951. ret.m_fileName = filename;
  952. return ret;
  953. }
  954.  
  955. /**
  956. Parses the given JSON string and returns the corresponding Json object.
  957.  
  958. Throws a JSONException if any parsing error occurs.
  959. */
  960. Json parseJsonString(string str, string filename = null)
  961. {
  962. import std.string : strip;
  963.  
  964. auto strcopy = str;
  965. int line = 0;
  966. auto ret = parseJson(strcopy, &line, filename);
  967. enforceJson(strcopy.strip().length == 0, "Expected end of string after JSON value.", filename, line);
  968. return ret;
  969. }
  970.  
  971. unittest {
  972. assert(parseJsonString("null") == Json(null));
  973. assert(parseJsonString("true") == Json(true));
  974. assert(parseJsonString("false") == Json(false));
  975. assert(parseJsonString("1") == Json(1));
  976. assert(parseJsonString("2.0") == Json(2.0));
  977. assert(parseJsonString("\"test\"") == Json("test"));
  978. assert(parseJsonString("[1, 2, 3]") == Json([Json(1), Json(2), Json(3)]));
  979. assert(parseJsonString("{\"a\": 1}") == Json(["a": Json(1)]));
  980. assert(parseJsonString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234");
  981. auto json = parseJsonString(`{"hey": "This is @à test éhééhhéhéé !%/??*&?\ud83d\udcec"}`);
  982. assert(json.toPrettyString() == parseJsonString(json.toPrettyString()).toPrettyString());
  983. }
  984.  
  985. unittest {
  986. import std.string : endsWith;
  987.  
  988. try parseJsonString(`{"a": 1`);
  989. catch (Exception e) assert(e.msg.endsWith("Missing '}' before EOF."));
  990. try parseJsonString(`{"a": 1 x`);
  991. catch (Exception e) assert(e.msg.endsWith("Expected '}' or ',' - got 'x'."));
  992. try parseJsonString(`[1`);
  993. catch (Exception e) assert(e.msg.endsWith("Missing ']' before EOF."));
  994. try parseJsonString(`[1 x`);
  995. catch (Exception e) assert(e.msg.endsWith("Expected ']' or ',' - got 'x'."));
  996. }
  997.  
  998. /**
  999. Serializes the given value to JSON.
  1000.  
  1001. The following types of values are supported:
  1002.  
  1003. $(DL
  1004. $(DT `Json`) $(DD Used as-is)
  1005. $(DT `null`) $(DD Converted to `Json.Type.null_`)
  1006. $(DT `bool`) $(DD Converted to `Json.Type.bool_`)
  1007. $(DT `float`, `double`) $(DD Converted to `Json.Type.float_`)
  1008. $(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `Json.Type.int_`)
  1009. $(DT `string`) $(DD Converted to `Json.Type.string`)
  1010. $(DT `T[]`) $(DD Converted to `Json.Type.array`)
  1011. $(DT `T[string]`) $(DD Converted to `Json.Type.object`)
  1012. $(DT `struct`) $(DD Converted to `Json.Type.object`)
  1013. $(DT `class`) $(DD Converted to `Json.Type.object` or `Json.Type.null_`)
  1014. )
  1015.  
  1016. All entries of an array or an associative array, as well as all R/W properties and
  1017. all public fields of a struct/class are recursively serialized using the same rules.
  1018.  
  1019. Fields ending with an underscore will have the last underscore stripped in the
  1020. serialized output. This makes it possible to use fields with D keywords as their name
  1021. by simply appending an underscore.
  1022.  
  1023. The following methods can be used to customize the serialization of structs/classes:
  1024.  
  1025. ---
  1026. Json toJson() const;
  1027. static T fromJson(Json src);
  1028.  
  1029. string toString() const;
  1030. static T fromString(string src);
  1031. ---
  1032.  
  1033. The methods will have to be defined in pairs. The first pair that is implemented by
  1034. the type will be used for serialization (i.e. `toJson` overrides `toString`).
  1035.  
  1036. See_Also: `deserializeJson`, `vibe.data.serialization`
  1037. */
  1038. Json serializeToJson(T)(T value)
  1039. {
  1040. version (VibeOldSerialization) {
  1041. return serializeToJsonOld(value);
  1042. } else {
  1043. return serialize!JsonSerializer(value);
  1044. }
  1045. }
  1046. /// ditto
  1047. void serializeToJson(R, T)(R destination, T value)
  1048. if (isOutputRange!(R, char) || isOutputRange!(R, ubyte))
  1049. {
  1050. serialize!(JsonStringSerializer!R)(value, destination);
  1051. }
  1052. /// ditto
  1053. string serializeToJsonString(T)(T value)
  1054. {
  1055. auto ret = appender!string;
  1056. serializeToJson(ret, value);
  1057. return ret.data;
  1058. }
  1059.  
  1060. ///
  1061. unittest {
  1062. struct Foo {
  1063. int number;
  1064. string str;
  1065. }
  1066.  
  1067. Foo f;
  1068. f.number = 12;
  1069. f.str = "hello";
  1070.  
  1071. string json = serializeToJsonString(f);
  1072. assert(json == `{"number":12,"str":"hello"}`);
  1073.  
  1074. Json jsonval = serializeToJson(f);
  1075. assert(jsonval.type == Json.Type.object);
  1076. assert(jsonval["number"] == Json(12));
  1077. assert(jsonval["str"] == Json("hello"));
  1078. }
  1079.  
  1080.  
  1081. /**
  1082. Serializes the given value to a pretty printed JSON string.
  1083.  
  1084. See_also: `serializeToJson`, `vibe.data.serialization`
  1085. */
  1086. void serializeToPrettyJson(R, T)(R destination, T value)
  1087. if (isOutputRange!(R, char) || isOutputRange!(R, ubyte))
  1088. {
  1089. serialize!(JsonStringSerializer!(R, true))(value, destination);
  1090. }
  1091. /// ditto
  1092. string serializeToPrettyJson(T)(T value)
  1093. {
  1094. auto ret = appender!string;
  1095. serializeToPrettyJson(ret, value);
  1096. return ret.data;
  1097. }
  1098.  
  1099. ///
  1100. unittest {
  1101. struct Foo {
  1102. int number;
  1103. string str;
  1104. }
  1105.  
  1106. Foo f;
  1107. f.number = 12;
  1108. f.str = "hello";
  1109.  
  1110. string json = serializeToPrettyJson(f);
  1111. assert(json ==
  1112. `{
  1113. "number": 12,
  1114. "str": "hello"
  1115. }`);
  1116. }
  1117.  
  1118.  
  1119. /// private
  1120. Json serializeToJsonOld(T)(T value)
  1121. {
  1122. import vibe.internal.meta.traits;
  1123.  
  1124. alias TU = Unqual!T;
  1125. static if (is(TU == Json)) return value;
  1126. else static if (is(TU == typeof(null))) return Json(null);
  1127. else static if (is(TU == bool)) return Json(value);
  1128. else static if (is(TU == float)) return Json(cast(double)value);
  1129. else static if (is(TU == double)) return Json(value);
  1130. else static if (is(TU == DateTime)) return Json(value.toISOExtString());
  1131. else static if (is(TU == SysTime)) return Json(value.toISOExtString());
  1132. else static if (is(TU == Date)) return Json(value.toISOExtString());
  1133. else static if (is(TU : long)) return Json(cast(long)value);
  1134. else static if (is(TU : string)) return Json(value);
  1135. else static if (isArray!T) {
  1136. auto ret = new Json[value.length];
  1137. foreach (i; 0 .. value.length)
  1138. ret[i] = serializeToJson(value[i]);
  1139. return Json(ret);
  1140. } else static if (isAssociativeArray!TU) {
  1141. Json[string] ret;
  1142. alias TK = KeyType!T;
  1143. foreach (key, value; value) {
  1144. static if(is(TK == string)) {
  1145. ret[key] = serializeToJson(value);
  1146. } else static if (is(TK == enum)) {
  1147. ret[to!string(key)] = serializeToJson(value);
  1148. } else static if (isStringSerializable!(TK)) {
  1149. ret[key.toString()] = serializeToJson(value);
  1150. } else static assert("AA key type %s not supported for JSON serialization.");
  1151. }
  1152. return Json(ret);
  1153. } else static if (isJsonSerializable!TU) {
  1154. return value.toJson();
  1155. } else static if (isStringSerializable!TU) {
  1156. return Json(value.toString());
  1157. } else static if (is(TU == struct)) {
  1158. Json[string] ret;
  1159. foreach (m; __traits(allMembers, T)) {
  1160. static if (isRWField!(TU, m)) {
  1161. auto mv = __traits(getMember, value, m);
  1162. ret[underscoreStrip(m)] = serializeToJson(mv);
  1163. }
  1164. }
  1165. return Json(ret);
  1166. } else static if(is(TU == class)) {
  1167. if (value is null) return Json(null);
  1168. Json[string] ret;
  1169. foreach (m; __traits(allMembers, T)) {
  1170. static if (isRWField!(TU, m)) {
  1171. auto mv = __traits(getMember, value, m);
  1172. ret[underscoreStrip(m)] = serializeToJson(mv);
  1173. }
  1174. }
  1175. return Json(ret);
  1176. } else static if (isPointer!TU) {
  1177. if (value is null) return Json(null);
  1178. return serializeToJson(*value);
  1179. } else {
  1180. static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization.");
  1181. }
  1182. }
  1183.  
  1184.  
  1185. /**
  1186. Deserializes a JSON value into the destination variable.
  1187.  
  1188. The same types as for `serializeToJson()` are supported and handled inversely.
  1189.  
  1190. See_Also: `serializeToJson`, `serializeToJsonString`, `vibe.data.serialization`
  1191. */
  1192. void deserializeJson(T)(ref T dst, Json src)
  1193. {
  1194. dst = deserializeJson!T(src);
  1195. }
  1196. /// ditto
  1197. T deserializeJson(T)(Json src)
  1198. {
  1199. version (VibeOldSerialization) {
  1200. return deserializeJsonOld!T(src);
  1201. } else {
  1202. return deserialize!(JsonSerializer, T)(src);
  1203. }
  1204. }
  1205. /// ditto
  1206. T deserializeJson(T, R)(R input)
  1207. if (isInputRange!R && !is(R == Json))
  1208. {
  1209. return deserialize!(JsonStringSerializer!R, T)(input);
  1210. }
  1211.  
  1212. /// private
  1213. T deserializeJsonOld(T)(Json src)
  1214. {
  1215. import vibe.internal.meta.traits;
  1216.  
  1217. static if( is(T == struct) || isSomeString!T || isIntegral!T || isFloatingPoint!T )
  1218. if( src.type == Json.Type.null_ ) return T.init;
  1219. static if (is(T == Json)) return src;
  1220. else static if (is(T == typeof(null))) { return null; }
  1221. else static if (is(T == bool)) return src.get!bool;
  1222. else static if (is(T == float)) return src.to!float; // since doubles are frequently serialized without
  1223. else static if (is(T == double)) return src.to!double; // a decimal point, we allow conversions here
  1224. else static if (is(T == DateTime)) return DateTime.fromISOExtString(src.get!string);
  1225. else static if (is(T == SysTime)) return SysTime.fromISOExtString(src.get!string);
  1226. else static if (is(T == Date)) return Date.fromISOExtString(src.get!string);
  1227. else static if (is(T : long)) return cast(T)src.get!long;
  1228. else static if (is(T : string)) return cast(T)src.get!string;
  1229. else static if (isArray!T) {
  1230. alias TV = typeof(T.init[0]) ;
  1231. auto dst = new Unqual!TV[src.length];
  1232. foreach (size_t i, v; src)
  1233. dst[i] = deserializeJson!(Unqual!TV)(v);
  1234. return cast(T)dst;
  1235. } else static if( isAssociativeArray!T ) {
  1236. alias TV = typeof(T.init.values[0]) ;
  1237. alias TK = KeyType!T;
  1238. Unqual!TV[TK] dst;
  1239. foreach (string key, value; src) {
  1240. static if (is(TK == string)) {
  1241. dst[key] = deserializeJson!(Unqual!TV)(value);
  1242. } else static if (is(TK == enum)) {
  1243. dst[to!(TK)(key)] = deserializeJson!(Unqual!TV)(value);
  1244. } else static if (isStringSerializable!TK) {
  1245. auto dsk = TK.fromString(key);
  1246. dst[dsk] = deserializeJson!(Unqual!TV)(value);
  1247. } else static assert("AA key type %s not supported for JSON serialization.");
  1248. }
  1249. return dst;
  1250. } else static if (isJsonSerializable!T) {
  1251. return T.fromJson(src);
  1252. } else static if (isStringSerializable!T) {
  1253. return T.fromString(src.get!string);
  1254. } else static if (is(T == struct)) {
  1255. T dst;
  1256. foreach (m; __traits(allMembers, T)) {
  1257. static if (isRWPlainField!(T, m) || isRWField!(T, m)) {
  1258. alias TM = typeof(__traits(getMember, dst, m)) ;
  1259. __traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]);
  1260. }
  1261. }
  1262. return dst;
  1263. } else static if (is(T == class)) {
  1264. if (src.type == Json.Type.null_) return null;
  1265. auto dst = new T;
  1266. foreach (m; __traits(allMembers, T)) {
  1267. static if (isRWPlainField!(T, m) || isRWField!(T, m)) {
  1268. alias TM = typeof(__traits(getMember, dst, m)) ;
  1269. __traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]);
  1270. }
  1271. }
  1272. return dst;
  1273. } else static if (isPointer!T) {
  1274. if (src.type == Json.Type.null_) return null;
  1275. alias TD = typeof(*T.init) ;
  1276. dst = new TD;
  1277. *dst = deserializeJson!TD(src);
  1278. return dst;
  1279. } else {
  1280. static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization.");
  1281. }
  1282. }
  1283.  
  1284. ///
  1285. unittest {
  1286. struct Foo {
  1287. int number;
  1288. string str;
  1289. }
  1290.  
  1291. Foo f = deserializeJson!Foo(`{"number": 12, "str": "hello"}`);
  1292. assert(f.number == 12);
  1293. assert(f.str == "hello");
  1294. }
  1295.  
  1296. unittest {
  1297. import std.stdio;
  1298. enum Foo : string { k = "test" }
  1299. enum Boo : int { l = 5 }
  1300. static struct S { float a; double b; bool c; int d; string e; byte f; ubyte g; long h; ulong i; float[] j; Foo k; Boo l; }
  1301. immutable S t = {1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [1.1, 1.2, 1.3], Foo.k, Boo.l};
  1302. S u;
  1303. deserializeJson(u, serializeToJson(t));
  1304. assert(t.a == u.a);
  1305. assert(t.b == u.b);
  1306. assert(t.c == u.c);
  1307. assert(t.d == u.d);
  1308. assert(t.e == u.e);
  1309. assert(t.f == u.f);
  1310. assert(t.g == u.g);
  1311. assert(t.h == u.h);
  1312. assert(t.i == u.i);
  1313. assert(t.j == u.j);
  1314. assert(t.k == u.k);
  1315. assert(t.l == u.l);
  1316. }
  1317.  
  1318. unittest
  1319. {
  1320. assert(uint.max == serializeToJson(uint.max).deserializeJson!uint);
  1321. assert(ulong.max == serializeToJson(ulong.max).deserializeJson!ulong);
  1322. }
  1323.  
  1324. unittest {
  1325. static struct A { int value; static A fromJson(Json val) { return A(val.get!int); } Json toJson() const { return Json(value); } }
  1326. static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } }
  1327. static struct D { int value; }
  1328.  
  1329. assert(serializeToJson(const A(123)) == Json(123));
  1330. assert(serializeToJson(A(123)) == Json(123));
  1331. assert(serializeToJson(const C(123)) == Json("123"));
  1332. assert(serializeToJson(C(123)) == Json("123"));
  1333. assert(serializeToJson(const D(123)) == serializeToJson(["value": 123]));
  1334. assert(serializeToJson(D(123)) == serializeToJson(["value": 123]));
  1335. }
  1336.  
  1337. unittest {
  1338. auto d = Date(2001,1,1);
  1339. deserializeJson(d, serializeToJson(Date.init));
  1340. assert(d == Date.init);
  1341. deserializeJson(d, serializeToJson(Date(2001,1,1)));
  1342. assert(d == Date(2001,1,1));
  1343. struct S { immutable(int)[] x; }
  1344. S s;
  1345. deserializeJson(s, serializeToJson(S([1,2,3])));
  1346. assert(s == S([1,2,3]));
  1347. struct T {
  1348. @optional S s;
  1349. @optional int i;
  1350. @optional float f_; // underscore strip feature
  1351. @optional double d;
  1352. @optional string str;
  1353. }
  1354. auto t = T(S([1,2,3]));
  1355. deserializeJson(t, parseJsonString(`{ "s" : null, "i" : null, "f" : null, "d" : null, "str" : null }`));
  1356. assert(text(t) == text(T()));
  1357. }
  1358.  
  1359. unittest {
  1360. static class C {
  1361. int a;
  1362. private int _b;
  1363. @property int b() const { return _b; }
  1364. @property void b(int v) { _b = v; }
  1365.  
  1366. @property int test() const { return 10; }
  1367.  
  1368. void test2() {}
  1369. }
  1370. C c = new C;
  1371. c.a = 1;
  1372. c.b = 2;
  1373.  
  1374. C d;
  1375. deserializeJson(d, serializeToJson(c));
  1376. assert(c.a == d.a);
  1377. assert(c.b == d.b);
  1378. }
  1379.  
  1380. unittest {
  1381. static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } }
  1382. enum Color { Red, Green, Blue }
  1383. {
  1384. static class T {
  1385. string[Color] enumIndexedMap;
  1386. string[C] stringableIndexedMap;
  1387. this() {
  1388. enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ];
  1389. stringableIndexedMap = [ C(42) : "forty-two" ];
  1390. }
  1391. }
  1392.  
  1393. T original = new T;
  1394. original.enumIndexedMap[Color.Green] = "olive";
  1395. T other;
  1396. deserializeJson(other, serializeToJson(original));
  1397. assert(serializeToJson(other) == serializeToJson(original));
  1398. }
  1399. {
  1400. static struct S {
  1401. string[Color] enumIndexedMap;
  1402. string[C] stringableIndexedMap;
  1403. }
  1404.  
  1405. S *original = new S;
  1406. original.enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ];
  1407. original.enumIndexedMap[Color.Green] = "olive";
  1408. original.stringableIndexedMap = [ C(42) : "forty-two" ];
  1409. S other;
  1410. deserializeJson(other, serializeToJson(original));
  1411. assert(serializeToJson(other) == serializeToJson(original));
  1412. }
  1413. }
  1414.  
  1415. unittest {
  1416. import std.typecons : Nullable;
  1417.  
  1418. struct S { Nullable!int a, b; }
  1419. S s;
  1420. s.a = 2;
  1421.  
  1422. auto j = serializeToJson(s);
  1423. assert(j["a"].type == Json.Type.int_);
  1424. assert(j["b"].type == Json.Type.null_);
  1425.  
  1426. auto t = deserializeJson!S(j);
  1427. assert(!t.a.isNull() && t.a == 2);
  1428. assert(t.b.isNull());
  1429. }
  1430.  
  1431. unittest { // #840
  1432. int[2][2] nestedArray = 1;
  1433. assert(nestedArray.serializeToJson.deserializeJson!(typeof(nestedArray)) == nestedArray);
  1434. }
  1435.  
  1436.  
  1437. /**
  1438. Serializer for a plain Json representation.
  1439.  
  1440. See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson
  1441. */
  1442. struct JsonSerializer {
  1443. template isJsonBasicType(T) { enum isJsonBasicType = isNumeric!T || isBoolean!T || is(T == string) || is(T == typeof(null)) || isJsonSerializable!T; }
  1444.  
  1445. template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json); }
  1446.  
  1447. private {
  1448. Json m_current;
  1449. Json[] m_compositeStack;
  1450. }
  1451.  
  1452. this(Json data) { m_current = data; }
  1453.  
  1454. @disable this(this);
  1455.  
  1456. //
  1457. // serialization
  1458. //
  1459. Json getSerializedResult() { return m_current; }
  1460. void beginWriteDictionary(T)() { m_compositeStack ~= Json.emptyObject; }
  1461. void endWriteDictionary(T)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; }
  1462. void beginWriteDictionaryEntry(T)(string name) {}
  1463. void endWriteDictionaryEntry(T)(string name) { m_compositeStack[$-1][name] = m_current; }
  1464.  
  1465. void beginWriteArray(T)(size_t) { m_compositeStack ~= Json.emptyArray; }
  1466. void endWriteArray(T)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; }
  1467. void beginWriteArrayEntry(T)(size_t) {}
  1468. void endWriteArrayEntry(T)(size_t) { m_compositeStack[$-1].appendArrayElement(m_current); }
  1469.  
  1470. void writeValue(T)(T value)
  1471. {
  1472. static if (is(T == Json)) m_current = value;
  1473. else static if (isJsonSerializable!T) m_current = value.toJson();
  1474. else m_current = Json(value);
  1475. }
  1476.  
  1477. void writeValue(T)(in Json value) if (is(T == Json))
  1478. {
  1479. m_current = value.clone;
  1480. }
  1481.  
  1482. //
  1483. // deserialization
  1484. //
  1485. void readDictionary(T)(scope void delegate(string) field_handler)
  1486. {
  1487. enforceJson(m_current.type == Json.Type.object, "Expected JSON object, got "~m_current.type.to!string);
  1488. auto old = m_current;
  1489. foreach (string key, value; m_current) {
  1490. m_current = value;
  1491. field_handler(key);
  1492. }
  1493. m_current = old;
  1494. }
  1495.  
  1496. void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
  1497. {
  1498. enforceJson(m_current.type == Json.Type.array, "Expected JSON array, got "~m_current.type.to!string);
  1499. auto old = m_current;
  1500. size_callback(m_current.length);
  1501. foreach (ent; old) {
  1502. m_current = ent;
  1503. entry_callback();
  1504. }
  1505. m_current = old;
  1506. }
  1507.  
  1508. T readValue(T)()
  1509. {
  1510. static if (is(T == Json)) return m_current;
  1511. else static if (isJsonSerializable!T) return T.fromJson(m_current);
  1512. else static if (is(T == float) || is(T == double)) {
  1513. if (m_current.type == Json.Type.undefined) return T.nan;
  1514. return m_current.type == Json.Type.float_ ? cast(T)m_current.get!double : cast(T)m_current.get!long;
  1515. }
  1516. else {
  1517. return m_current.get!T();
  1518. }
  1519. }
  1520.  
  1521. bool tryReadNull() { return m_current.type == Json.Type.null_; }
  1522. }
  1523.  
  1524.  
  1525. /**
  1526. Serializer for a range based plain JSON string representation.
  1527.  
  1528. See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson
  1529. */
  1530. struct JsonStringSerializer(R, bool pretty = false)
  1531. if (isInputRange!R || isOutputRange!(R, char))
  1532. {
  1533. private {
  1534. R m_range;
  1535. size_t m_level = 0;
  1536. }
  1537.  
  1538. template isJsonBasicType(T) { enum isJsonBasicType = isNumeric!T || isBoolean!T || is(T == string) || is(T == typeof(null)) || isJsonSerializable!T; }
  1539.  
  1540. template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json); }
  1541.  
  1542. this(R range)
  1543. {
  1544. m_range = range;
  1545. }
  1546.  
  1547. @disable this(this);
  1548.  
  1549. //
  1550. // serialization
  1551. //
  1552. static if (isOutputRange!(R, char)) {
  1553. private {
  1554. bool m_firstInComposite;
  1555. }
  1556.  
  1557. void getSerializedResult() {}
  1558.  
  1559. void beginWriteDictionary(T)() { startComposite(); m_range.put('{'); }
  1560. void endWriteDictionary(T)() { endComposite(); m_range.put("}"); }
  1561. void beginWriteDictionaryEntry(T)(string name)
  1562. {
  1563. startCompositeEntry();
  1564. m_range.put('"');
  1565. m_range.jsonEscape(name);
  1566. static if (pretty) m_range.put(`": `);
  1567. else m_range.put(`":`);
  1568. }
  1569. void endWriteDictionaryEntry(T)(string name) {}
  1570.  
  1571. void beginWriteArray(T)(size_t) { startComposite(); m_range.put('['); }
  1572. void endWriteArray(T)() { endComposite(); m_range.put(']'); }
  1573. void beginWriteArrayEntry(T)(size_t) { startCompositeEntry(); }
  1574. void endWriteArrayEntry(T)(size_t) {}
  1575.  
  1576. void writeValue(T)(in T value)
  1577. {
  1578. static if (is(T == typeof(null))) m_range.put("null");
  1579. else static if (is(T == bool)) m_range.put(value ? "true" : "false");
  1580. else static if (is(T : long)) m_range.formattedWrite("%s", value);
  1581. else static if (is(T : real)) m_range.formattedWrite("%.16g", value);
  1582. else static if (is(T == string)) {
  1583. m_range.put('"');
  1584. m_range.jsonEscape(value);
  1585. m_range.put('"');
  1586. }
  1587. else static if (is(T == Json)) m_range.writeJsonString(value);
  1588. else static if (isJsonSerializable!T) m_range.writeJsonString!(R, pretty)(value.toJson(), m_level);
  1589. else static assert(false, "Unsupported type: " ~ T.stringof);
  1590. }
  1591.  
  1592. private void startComposite()
  1593. {
  1594. static if (pretty) m_level++;
  1595. m_firstInComposite = true;
  1596. }
  1597.  
  1598. private void startCompositeEntry()
  1599. {
  1600. if (!m_firstInComposite) {
  1601. m_range.put(',');
  1602. } else {
  1603. m_firstInComposite = false;
  1604. }
  1605. static if (pretty) indent();
  1606. }
  1607.  
  1608. private void endComposite()
  1609. {
  1610. static if (pretty) {
  1611. m_level--;
  1612. if (!m_firstInComposite) indent();
  1613. }
  1614. m_firstInComposite = false;
  1615. }
  1616.  
  1617. private void indent()
  1618. {
  1619. m_range.put('\n');
  1620. foreach (i; 0 .. m_level) m_range.put('\t');
  1621. }
  1622. }
  1623.  
  1624. //
  1625. // deserialization
  1626. //
  1627. static if (isInputRange!(R)) {
  1628. private {
  1629. int m_line = 0;
  1630. }
  1631.  
  1632. void readDictionary(T)(scope void delegate(string) entry_callback)
  1633. {
  1634. m_range.skipWhitespace(&m_line);
  1635. enforceJson(!m_range.empty && m_range.front == '{', "Expecting object.");
  1636. m_range.popFront();
  1637. bool first = true;
  1638. while(true) {
  1639. m_range.skipWhitespace(&m_line);
  1640. enforceJson(!m_range.empty, "Missing '}'.");
  1641. if (m_range.front == '}') {
  1642. m_range.popFront();
  1643. break;
  1644. } else if (!first) {
  1645. enforceJson(m_range.front == ',', "Expecting ',' or '}', not '"~m_range.front.to!string~"'.");
  1646. m_range.popFront();
  1647. m_range.skipWhitespace(&m_line);
  1648. } else first = false;
  1649.  
  1650. auto name = m_range.skipJsonString(null, &m_line);
  1651.  
  1652. m_range.skipWhitespace(&m_line);
  1653. enforceJson(!m_range.empty && m_range.front == ':', "Expecting ':', not '"~m_range.front.to!string~"'.");
  1654. m_range.popFront();
  1655.  
  1656. entry_callback(name);
  1657. }
  1658. }
  1659.  
  1660. void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
  1661. {
  1662. m_range.skipWhitespace(&m_line);
  1663. enforceJson(!m_range.empty && m_range.front == '[', "Expecting array.");
  1664. m_range.popFront();
  1665. bool first = true;
  1666. while(true) {
  1667. m_range.skipWhitespace(&m_line);
  1668. enforceJson(!m_range.empty, "Missing ']'.");
  1669. if (m_range.front == ']') {
  1670. m_range.popFront();
  1671. break;
  1672. } else if (!first) {
  1673. enforceJson(m_range.front == ',', "Expecting ',' or ']'.");
  1674. m_range.popFront();
  1675. } else first = false;
  1676.  
  1677. entry_callback();
  1678. }
  1679. }
  1680.  
  1681. T readValue(T)()
  1682. {
  1683. m_range.skipWhitespace(&m_line);
  1684. static if (is(T == typeof(null))) { enforceJson(m_range.take(4).equal("null"), "Expecting 'null'."); return null; }
  1685. else static if (is(T == bool)) {
  1686. bool ret = m_range.front == 't';
  1687. string expected = ret ? "true" : "false";
  1688. foreach (ch; expected) {
  1689. enforceJson(m_range.front == ch, "Expecting 'true' or 'false'.");
  1690. m_range.popFront();
  1691. }
  1692. return ret;
  1693. } else static if (is(T : long)) {
  1694. bool is_float;
  1695. auto num = m_range.skipNumber(is_float, null, &m_line);
  1696. enforceJson(!is_float, "Expecting integer number.");
  1697. return to!T(num);
  1698. } else static if (is(T : real)) {
  1699. bool is_float;
  1700. auto num = m_range.skipNumber(is_float);
  1701. return to!T(num);
  1702. }
  1703. else static if (is(T == string)) return m_range.skipJsonString(null, &m_line);
  1704. else static if (is(T == Json)) return m_range.parseJson(&m_line);
  1705. else static if (isJsonSerializable!T) return T.fromJson(m_range.parseJson(&m_line));
  1706. else static assert(false, "Unsupported type: " ~ T.stringof);
  1707. }
  1708.  
  1709. bool tryReadNull()
  1710. {
  1711. m_range.skipWhitespace(&m_line);
  1712. if (m_range.front != 'n') return false;
  1713. foreach (ch; "null") {
  1714. enforceJson(m_range.front == ch, "Expecting 'null'.");
  1715. m_range.popFront();
  1716. }
  1717. assert(m_range.empty || m_range.front != 'l');
  1718. return true;
  1719. }
  1720. }
  1721. }
  1722.  
  1723.  
  1724.  
  1725. /**
  1726. Writes the given JSON object as a JSON string into the destination range.
  1727.  
  1728. This function will convert the given JSON value to a string without adding
  1729. any white space between tokens (no newlines, no indentation and no padding).
  1730. The output size is thus minimized, at the cost of bad human readability.
  1731.  
  1732. Params:
  1733. dst = References the string output range to which the result is written.
  1734. json = Specifies the JSON value that is to be stringified.
  1735.  
  1736. See_Also: Json.toString, writePrettyJsonString
  1737. */
  1738. void writeJsonString(R, bool pretty = false)(ref R dst, in Json json, size_t level = 0)
  1739. // if( isOutputRange!R && is(ElementEncodingType!R == char) )
  1740. {
  1741. final switch( json.type ){
  1742. case Json.Type.undefined: dst.put("undefined"); break;
  1743. case Json.Type.null_: dst.put("null"); break;
  1744. case Json.Type.bool_: dst.put(cast(bool)json ? "true" : "false"); break;
  1745. case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break;
  1746. case Json.Type.float_:
  1747. auto d = json.get!double;
  1748. if (d != d)
  1749. dst.put("undefined"); // JSON has no NaN value so set null
  1750. else
  1751. formattedWrite(dst, "%.16g", json.get!double);
  1752. break;
  1753. case Json.Type.string:
  1754. dst.put('\"');
  1755. jsonEscape(dst, cast(string)json);
  1756. dst.put('\"');
  1757. break;
  1758. case Json.Type.array:
  1759. dst.put('[');
  1760. bool first = true;
  1761. foreach (ref const Json e; json) {
  1762. if( !first ) dst.put(",");
  1763. first = false;
  1764. static if (pretty) {
  1765. dst.put('\n');
  1766. foreach (tab; 0 .. level+1) dst.put('\t');
  1767. }
  1768. if (e.type == Json.Type.undefined) dst.put("null");
  1769. else writeJsonString!(R, pretty)(dst, e, level+1);
  1770. }
  1771. static if (pretty) {
  1772. if (json.length > 0) {
  1773. dst.put('\n');
  1774. foreach (tab; 0 .. level) dst.put('\t');
  1775. }
  1776. }
  1777. dst.put(']');
  1778. break;
  1779. case Json.Type.object:
  1780. dst.put('{');
  1781. bool first = true;
  1782. foreach( string k, ref const Json e; json ){
  1783. if( e.type == Json.Type.undefined ) continue;
  1784. if( !first ) dst.put(',');
  1785. first = false;
  1786. static if (pretty) {
  1787. dst.put('\n');
  1788. foreach (tab; 0 .. level+1) dst.put('\t');
  1789. }
  1790. dst.put('\"');
  1791. jsonEscape(dst, k);
  1792. dst.put(pretty ? `": ` : `":`);
  1793. writeJsonString!(R, pretty)(dst, e, level+1);
  1794. }
  1795. static if (pretty) {
  1796. if (json.length > 0) {
  1797. dst.put('\n');
  1798. foreach (tab; 0 .. level) dst.put('\t');
  1799. }
  1800. }
  1801. dst.put('}');
  1802. break;
  1803. }
  1804. }
  1805.  
  1806. unittest {
  1807. auto a = Json.emptyObject;
  1808. a["a"] = Json.emptyArray;
  1809. a["b"] = Json.emptyArray;
  1810. a["b"] ~= Json(1);
  1811. a["b"] ~= Json.emptyObject;
  1812.  
  1813. assert(a.toString() == `{"a":[],"b":[1,{}]}` || a.toString == `{"b":[1,{}],"a":[]}`);
  1814. assert(a.toPrettyString() ==
  1815. `{
  1816. "a": [],
  1817. "b": [
  1818. 1,
  1819. {}
  1820. ]
  1821. }` || a.toPrettyString() ==
  1822. `{
  1823. "b": [
  1824. 1,
  1825. {}
  1826. ],
  1827. "a": []
  1828. }`);
  1829. }
  1830.  
  1831. unittest { // #735
  1832. auto a = Json.emptyArray;
  1833. a ~= "a";
  1834. a ~= Json();
  1835. a ~= "b";
  1836. a ~= null;
  1837. a ~= "c";
  1838. assert(a.toString() == `["a",null,"b",null,"c"]`);
  1839. }
  1840.  
  1841. unittest {
  1842. auto a = Json.emptyArray;
  1843. a ~= Json(1);
  1844. a ~= Json(2);
  1845. a ~= Json(3);
  1846. a ~= Json(4);
  1847. a ~= Json(5);
  1848.  
  1849. auto b = Json(a[0..a.length]);
  1850. assert(a == b);
  1851.  
  1852. auto c = Json(a[0..$]);
  1853. assert(a == c);
  1854. assert(b == c);
  1855.  
  1856. auto d = [Json(1),Json(2),Json(3)];
  1857. assert(d == a[0..a.length-2]);
  1858. assert(d == a[0..$-2]);
  1859. }
  1860.  
  1861. unittest {
  1862. auto j = Json(double.init);
  1863.  
  1864. assert(j.toString == "undefined"); // A double nan should serialize to undefined
  1865. j = 17.04f;
  1866. assert(j.toString == "17.04"); // A proper double should serialize correctly
  1867.  
  1868. double d;
  1869. deserializeJson(d, Json.undefined); // Json.undefined should deserialize to nan
  1870. assert(d != d);
  1871. }
  1872. /**
  1873. Writes the given JSON object as a prettified JSON string into the destination range.
  1874.  
  1875. The output will contain newlines and indents to make the output human readable.
  1876.  
  1877. Params:
  1878. dst = References the string output range to which the result is written.
  1879. json = Specifies the JSON value that is to be stringified.
  1880. level = Specifies the base amount of indentation for the output. Indentation is always
  1881. done using tab characters.
  1882.  
  1883. See_Also: Json.toPrettyString, writeJsonString
  1884. */
  1885. void writePrettyJsonString(R)(ref R dst, in Json json, int level = 0)
  1886. // if( isOutputRange!R && is(ElementEncodingType!R == char) )
  1887. {
  1888. writeJsonString!(R, true)(dst, json, level);
  1889. }
  1890.  
  1891.  
  1892. /**
  1893. Helper function that escapes all Unicode characters in a JSON string.
  1894. */
  1895. string convertJsonToASCII(string json)
  1896. {
  1897. auto ret = appender!string;
  1898. jsonEscape!true(ret, json);
  1899. return ret.data;
  1900. }
  1901.  
  1902.  
  1903. /// private
  1904. private void jsonEscape(bool escape_unicode = false, R)(ref R dst, string s)
  1905. {
  1906. for (size_t pos = 0; pos < s.length; pos++) {
  1907. immutable(char) ch = s[pos];
  1908.  
  1909. switch (ch) {
  1910. default:
  1911. static if (escape_unicode) {
  1912. if (ch > 0x20 && ch < 0x80) dst.put(ch);
  1913. else {
  1914. import std.utf : decode;
  1915. char[13] buf;
  1916. int len;
  1917. dchar codepoint = decode(s, pos);
  1918. import std.c.stdio : sprintf;
  1919. /* codepoint is in BMP */
  1920. if(codepoint < 0x10000)
  1921. {
  1922. sprintf(&buf[0], "\\u%04X", codepoint);
  1923. len = 6;
  1924. }
  1925. /* not in BMP -> construct a UTF-16 surrogate pair */
  1926. else
  1927. {
  1928. int first, last;
  1929.  
  1930. codepoint -= 0x10000;
  1931. first = 0xD800 | ((codepoint & 0xffc00) >> 10);
  1932. last = 0xDC00 | (codepoint & 0x003ff);
  1933.  
  1934. sprintf(&buf[0], "\\u%04X\\u%04X", first, last);
  1935. len = 12;
  1936. }
  1937.  
  1938. pos -= 1;
  1939. foreach (i; 0 .. len)
  1940. dst.put(buf[i]);
  1941.  
  1942. }
  1943. } else {
  1944. if (ch < 0x20) dst.formattedWrite("\\u%04X", ch);
  1945. else dst.put(ch);
  1946. }
  1947. break;
  1948. case '\\': dst.put("\\\\"); break;
  1949. case '\r': dst.put("\\r"); break;
  1950. case '\n': dst.put("\\n"); break;
  1951. case '\t': dst.put("\\t"); break;
  1952. case '\"': dst.put("\\\""); break;
  1953. }
  1954. }
  1955. }
  1956.  
  1957. /// private
  1958. private string jsonUnescape(R)(ref R range, string filename, int* line)
  1959. {
  1960. auto ret = appender!string();
  1961. while(!range.empty){
  1962. auto ch = range.front;
  1963. switch( ch ){
  1964. case '"': return ret.data;
  1965. case '\\':
  1966. range.popFront();
  1967. enforceJson(!range.empty, "Unterminated string escape sequence.", filename, line);
  1968. switch(range.front){
  1969. default: enforceJson(false, "Invalid string escape sequence.", filename, line); break;
  1970. case '"': ret.put('\"'); range.popFront(); break;
  1971. case '\\': ret.put('\\'); range.popFront(); break;
  1972. case '/': ret.put('/'); range.popFront(); break;
  1973. case 'b': ret.put('\b'); range.popFront(); break;
  1974. case 'f': ret.put('\f'); range.popFront(); break;
  1975. case 'n': ret.put('\n'); range.popFront(); break;
  1976. case 'r': ret.put('\r'); range.popFront(); break;
  1977. case 't': ret.put('\t'); range.popFront(); break;
  1978. case 'u':
  1979.  
  1980. dchar decode_unicode_escape() {
  1981. enforceJson(range.front == 'u');
  1982. range.popFront();
  1983. dchar uch = 0;
  1984. foreach( i; 0 .. 4 ){
  1985. uch *= 16;
  1986. enforceJson(!range.empty, "Unicode sequence must be '\\uXXXX'.", filename, line);
  1987. auto dc = range.front;
  1988. range.popFront();
  1989.  
  1990. if( dc >= '0' && dc <= '9' ) uch += dc - '0';
  1991. else if( dc >= 'a' && dc <= 'f' ) uch += dc - 'a' + 10;
  1992. else if( dc >= 'A' && dc <= 'F' ) uch += dc - 'A' + 10;
  1993. else enforceJson(false, "Unicode sequence must be '\\uXXXX'.", filename, line);
  1994. }
  1995. return uch;
  1996. }
  1997.  
  1998. auto uch = decode_unicode_escape();
  1999.  
  2000. if(0xD800 <= uch && uch <= 0xDBFF) {
  2001. /* surrogate pair */
  2002. range.popFront(); // backslash '\'
  2003. auto uch2 = decode_unicode_escape();
  2004. enforceJson(0xDC00 <= uch2 && uch2 <= 0xDFFF, "invalid Unicode", filename, line);
  2005. {
  2006. /* valid second surrogate */
  2007. uch =
  2008. ((uch - 0xD800) << 10) +
  2009. (uch2 - 0xDC00) +
  2010. 0x10000;
  2011. }
  2012. }
  2013. ret.put(uch);
  2014. break;
  2015. }
  2016. break;
  2017. default:
  2018. ret.put(ch);
  2019. range.popFront();
  2020. break;
  2021. }
  2022. }
  2023. return ret.data;
  2024. }
  2025.  
  2026. /// private
  2027. private string skipNumber(R)(ref R s, out bool is_float, string filename, int* line)
  2028. {
  2029. // TODO: make this work with input ranges
  2030. size_t idx = 0;
  2031. is_float = false;
  2032. if (s[idx] == '-') idx++;
  2033. if (s[idx] == '0') idx++;
  2034. else {
  2035. enforceJson(isDigit(s[idx++]), "Digit expected at beginning of number.", filename, line);
  2036. while( idx < s.length && isDigit(s[idx]) ) idx++;
  2037. }
  2038.  
  2039. if( idx < s.length && s[idx] == '.' ){
  2040. idx++;
  2041. is_float = true;
  2042. while( idx < s.length && isDigit(s[idx]) ) idx++;
  2043. }
  2044.  
  2045. if( idx < s.length && (s[idx] == 'e' || s[idx] == 'E') ){
  2046. idx++;
  2047. is_float = true;
  2048. if( idx < s.length && (s[idx] == '+' || s[idx] == '-') ) idx++;
  2049. enforceJson( idx < s.length && isDigit(s[idx]), "Expected exponent." ~ s[0 .. idx], filename, line);
  2050. idx++;
  2051. while( idx < s.length && isDigit(s[idx]) ) idx++;
  2052. }
  2053.  
  2054. string ret = s[0 .. idx];
  2055. s = s[idx .. $];
  2056. return ret;
  2057. }
  2058.  
  2059. /// private
  2060. private string skipJsonString(R)(ref R s, string filename, int* line)
  2061. {
  2062. // TODO: count or disallow any newlines inside of the string
  2063. enforceJson(!s.empty && s.front == '"', "Expected '\"' to start string.", filename, line);
  2064. s.popFront();
  2065. string ret = jsonUnescape(s, filename, line);
  2066. enforceJson(!s.empty && s.front == '"', "Expected '\"' to terminate string.", filename, line);
  2067. s.popFront();
  2068. return ret;
  2069. }
  2070.  
  2071. /// private
  2072. private void skipWhitespace(R)(ref R s, int* line = null)
  2073. {
  2074. while (!s.empty) {
  2075. switch (s.front) {
  2076. default: return;
  2077. case ' ', '\t': s.popFront(); break;
  2078. case '\n':
  2079. s.popFront();
  2080. if (!s.empty && s.front == '\r') s.popFront();
  2081. if (line) (*line)++;
  2082. break;
  2083. case '\r':
  2084. s.popFront();
  2085. if (!s.empty && s.front == '\n') s.popFront();
  2086. if (line) (*line)++;
  2087. break;
  2088. }
  2089. }
  2090. }
  2091.  
  2092. private bool isDigit(dchar ch) { return ch >= '0' && ch <= '9'; }
  2093.  
  2094. private string underscoreStrip(string field_name)
  2095. {
  2096. if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
  2097. else return field_name[0 .. $-1];
  2098. }
  2099.  
  2100. /// private
  2101. package template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) == Json) && is(typeof(T.fromJson(Json())) == T); }
  2102.  
  2103. private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message = "JSON exception")
  2104. {
  2105. static if (__VERSION__ >= 2065) enforceEx!JSONException(cond, message, file, line);
  2106. else if (!cond) throw new JSONException(message);
  2107. }
  2108.  
  2109. private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int err_line)
  2110. {
  2111. auto errmsg() { return format("%s(%s): Error: %s", err_file, err_line+1, message); }
  2112. static if (__VERSION__ >= 2065) enforceEx!JSONException(cond, errmsg, file, line);
  2113. else if (!cond) throw new JSONException(errmsg);
  2114. }
  2115.  
  2116. private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int* err_line)
  2117. {
  2118. enforceJson!(file, line)(cond, message, err_file, err_line ? *err_line : -1);
  2119. }