Fix build on sparc64-linux-gnu.
[official-gcc.git] / libphobos / src / std / json.d
blobfd6cf41968c569bd247f5cea1495ef4d2b972a38
1 // Written in the D programming language.
3 /**
4 JavaScript Object Notation
6 Copyright: Copyright Jeremie Pelletier 2008 - 2009.
7 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
8 Authors: Jeremie Pelletier, David Herberth
9 References: $(LINK http://json.org/)
10 Source: $(PHOBOSSRC std/_json.d)
13 Copyright Jeremie Pelletier 2008 - 2009.
14 Distributed under the Boost Software License, Version 1.0.
15 (See accompanying file LICENSE_1_0.txt or copy at
16 http://www.boost.org/LICENSE_1_0.txt)
18 module std.json;
20 import std.array;
21 import std.conv;
22 import std.range.primitives;
23 import std.traits;
25 ///
26 @system unittest
28 import std.conv : to;
30 // parse a file or string of json into a usable structure
31 string s = `{ "language": "D", "rating": 3.5, "code": "42" }`;
32 JSONValue j = parseJSON(s);
33 // j and j["language"] return JSONValue,
34 // j["language"].str returns a string
35 assert(j["language"].str == "D");
36 assert(j["rating"].floating == 3.5);
38 // check a type
39 long x;
40 if (const(JSONValue)* code = "code" in j)
42 if (code.type() == JSON_TYPE.INTEGER)
43 x = code.integer;
44 else
45 x = to!int(code.str);
48 // create a json struct
49 JSONValue jj = [ "language": "D" ];
50 // rating doesnt exist yet, so use .object to assign
51 jj.object["rating"] = JSONValue(3.5);
52 // create an array to assign to list
53 jj.object["list"] = JSONValue( ["a", "b", "c"] );
54 // list already exists, so .object optional
55 jj["list"].array ~= JSONValue("D");
57 string jjStr = `{"language":"D","list":["a","b","c","D"],"rating":3.5}`;
58 assert(jj.toString == jjStr);
61 /**
62 String literals used to represent special float values within JSON strings.
64 enum JSONFloatLiteral : string
66 nan = "NaN", /// string representation of floating-point NaN
67 inf = "Infinite", /// string representation of floating-point Infinity
68 negativeInf = "-Infinite", /// string representation of floating-point negative Infinity
71 /**
72 Flags that control how json is encoded and parsed.
74 enum JSONOptions
76 none, /// standard parsing
77 specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings
78 escapeNonAsciiChars = 0x2, /// encode non ascii characters with an unicode escape sequence
79 doNotEscapeSlashes = 0x4, /// do not escape slashes ('/')
82 /**
83 JSON type enumeration
85 enum JSON_TYPE : byte
87 /// Indicates the type of a $(D JSONValue).
88 NULL,
89 STRING, /// ditto
90 INTEGER, /// ditto
91 UINTEGER,/// ditto
92 FLOAT, /// ditto
93 OBJECT, /// ditto
94 ARRAY, /// ditto
95 TRUE, /// ditto
96 FALSE /// ditto
99 /**
100 JSON value node
102 struct JSONValue
104 import std.exception : enforceEx, enforce;
106 union Store
108 string str;
109 long integer;
110 ulong uinteger;
111 double floating;
112 JSONValue[string] object;
113 JSONValue[] array;
115 private Store store;
116 private JSON_TYPE type_tag;
119 Returns the JSON_TYPE of the value stored in this structure.
121 @property JSON_TYPE type() const pure nothrow @safe @nogc
123 return type_tag;
126 @safe unittest
128 string s = "{ \"language\": \"D\" }";
129 JSONValue j = parseJSON(s);
130 assert(j.type == JSON_TYPE.OBJECT);
131 assert(j["language"].type == JSON_TYPE.STRING);
134 /***
135 * Value getter/setter for $(D JSON_TYPE.STRING).
136 * Throws: $(D JSONException) for read access if $(D type) is not
137 * $(D JSON_TYPE.STRING).
139 @property string str() const pure @trusted
141 enforce!JSONException(type == JSON_TYPE.STRING,
142 "JSONValue is not a string");
143 return store.str;
145 /// ditto
146 @property string str(string v) pure nothrow @nogc @safe
148 assign(v);
149 return v;
152 @safe unittest
154 JSONValue j = [ "language": "D" ];
156 // get value
157 assert(j["language"].str == "D");
159 // change existing key to new string
160 j["language"].str = "Perl";
161 assert(j["language"].str == "Perl");
164 /***
165 * Value getter/setter for $(D JSON_TYPE.INTEGER).
166 * Throws: $(D JSONException) for read access if $(D type) is not
167 * $(D JSON_TYPE.INTEGER).
169 @property inout(long) integer() inout pure @safe
171 enforce!JSONException(type == JSON_TYPE.INTEGER,
172 "JSONValue is not an integer");
173 return store.integer;
175 /// ditto
176 @property long integer(long v) pure nothrow @safe @nogc
178 assign(v);
179 return store.integer;
182 /***
183 * Value getter/setter for $(D JSON_TYPE.UINTEGER).
184 * Throws: $(D JSONException) for read access if $(D type) is not
185 * $(D JSON_TYPE.UINTEGER).
187 @property inout(ulong) uinteger() inout pure @safe
189 enforce!JSONException(type == JSON_TYPE.UINTEGER,
190 "JSONValue is not an unsigned integer");
191 return store.uinteger;
193 /// ditto
194 @property ulong uinteger(ulong v) pure nothrow @safe @nogc
196 assign(v);
197 return store.uinteger;
200 /***
201 * Value getter/setter for $(D JSON_TYPE.FLOAT). Note that despite
202 * the name, this is a $(B 64)-bit `double`, not a 32-bit `float`.
203 * Throws: $(D JSONException) for read access if $(D type) is not
204 * $(D JSON_TYPE.FLOAT).
206 @property inout(double) floating() inout pure @safe
208 enforce!JSONException(type == JSON_TYPE.FLOAT,
209 "JSONValue is not a floating type");
210 return store.floating;
212 /// ditto
213 @property double floating(double v) pure nothrow @safe @nogc
215 assign(v);
216 return store.floating;
219 /***
220 * Value getter/setter for $(D JSON_TYPE.OBJECT).
221 * Throws: $(D JSONException) for read access if $(D type) is not
222 * $(D JSON_TYPE.OBJECT).
223 * Note: this is @system because of the following pattern:
225 auto a = &(json.object());
226 json.uinteger = 0; // overwrite AA pointer
227 (*a)["hello"] = "world"; // segmentation fault
230 @property ref inout(JSONValue[string]) object() inout pure @system
232 enforce!JSONException(type == JSON_TYPE.OBJECT,
233 "JSONValue is not an object");
234 return store.object;
236 /// ditto
237 @property JSONValue[string] object(JSONValue[string] v) pure nothrow @nogc @safe
239 assign(v);
240 return v;
243 /***
244 * Value getter for $(D JSON_TYPE.OBJECT).
245 * Unlike $(D object), this retrieves the object by value and can be used in @safe code.
247 * A caveat is that, if the returned value is null, modifications will not be visible:
248 * ---
249 * JSONValue json;
250 * json.object = null;
251 * json.objectNoRef["hello"] = JSONValue("world");
252 * assert("hello" !in json.object);
253 * ---
255 * Throws: $(D JSONException) for read access if $(D type) is not
256 * $(D JSON_TYPE.OBJECT).
258 @property inout(JSONValue[string]) objectNoRef() inout pure @trusted
260 enforce!JSONException(type == JSON_TYPE.OBJECT,
261 "JSONValue is not an object");
262 return store.object;
265 /***
266 * Value getter/setter for $(D JSON_TYPE.ARRAY).
267 * Throws: $(D JSONException) for read access if $(D type) is not
268 * $(D JSON_TYPE.ARRAY).
269 * Note: this is @system because of the following pattern:
271 auto a = &(json.array());
272 json.uinteger = 0; // overwrite array pointer
273 (*a)[0] = "world"; // segmentation fault
276 @property ref inout(JSONValue[]) array() inout pure @system
278 enforce!JSONException(type == JSON_TYPE.ARRAY,
279 "JSONValue is not an array");
280 return store.array;
282 /// ditto
283 @property JSONValue[] array(JSONValue[] v) pure nothrow @nogc @safe
285 assign(v);
286 return v;
289 /***
290 * Value getter for $(D JSON_TYPE.ARRAY).
291 * Unlike $(D array), this retrieves the array by value and can be used in @safe code.
293 * A caveat is that, if you append to the returned array, the new values aren't visible in the
294 * JSONValue:
295 * ---
296 * JSONValue json;
297 * json.array = [JSONValue("hello")];
298 * json.arrayNoRef ~= JSONValue("world");
299 * assert(json.array.length == 1);
300 * ---
302 * Throws: $(D JSONException) for read access if $(D type) is not
303 * $(D JSON_TYPE.ARRAY).
305 @property inout(JSONValue[]) arrayNoRef() inout pure @trusted
307 enforce!JSONException(type == JSON_TYPE.ARRAY,
308 "JSONValue is not an array");
309 return store.array;
312 /// Test whether the type is $(D JSON_TYPE.NULL)
313 @property bool isNull() const pure nothrow @safe @nogc
315 return type == JSON_TYPE.NULL;
318 private void assign(T)(T arg) @safe
320 static if (is(T : typeof(null)))
322 type_tag = JSON_TYPE.NULL;
324 else static if (is(T : string))
326 type_tag = JSON_TYPE.STRING;
327 string t = arg;
328 () @trusted { store.str = t; }();
330 else static if (isSomeString!T) // issue 15884
332 type_tag = JSON_TYPE.STRING;
333 // FIXME: std.array.array(Range) is not deduced as 'pure'
334 () @trusted {
335 import std.utf : byUTF;
336 store.str = cast(immutable)(arg.byUTF!char.array);
337 }();
339 else static if (is(T : bool))
341 type_tag = arg ? JSON_TYPE.TRUE : JSON_TYPE.FALSE;
343 else static if (is(T : ulong) && isUnsigned!T)
345 type_tag = JSON_TYPE.UINTEGER;
346 store.uinteger = arg;
348 else static if (is(T : long))
350 type_tag = JSON_TYPE.INTEGER;
351 store.integer = arg;
353 else static if (isFloatingPoint!T)
355 type_tag = JSON_TYPE.FLOAT;
356 store.floating = arg;
358 else static if (is(T : Value[Key], Key, Value))
360 static assert(is(Key : string), "AA key must be string");
361 type_tag = JSON_TYPE.OBJECT;
362 static if (is(Value : JSONValue))
364 JSONValue[string] t = arg;
365 () @trusted { store.object = t; }();
367 else
369 JSONValue[string] aa;
370 foreach (key, value; arg)
371 aa[key] = JSONValue(value);
372 () @trusted { store.object = aa; }();
375 else static if (isArray!T)
377 type_tag = JSON_TYPE.ARRAY;
378 static if (is(ElementEncodingType!T : JSONValue))
380 JSONValue[] t = arg;
381 () @trusted { store.array = t; }();
383 else
385 JSONValue[] new_arg = new JSONValue[arg.length];
386 foreach (i, e; arg)
387 new_arg[i] = JSONValue(e);
388 () @trusted { store.array = new_arg; }();
391 else static if (is(T : JSONValue))
393 type_tag = arg.type;
394 store = arg.store;
396 else
398 static assert(false, text(`unable to convert type "`, T.stringof, `" to json`));
402 private void assignRef(T)(ref T arg) if (isStaticArray!T)
404 type_tag = JSON_TYPE.ARRAY;
405 static if (is(ElementEncodingType!T : JSONValue))
407 store.array = arg;
409 else
411 JSONValue[] new_arg = new JSONValue[arg.length];
412 foreach (i, e; arg)
413 new_arg[i] = JSONValue(e);
414 store.array = new_arg;
419 * Constructor for $(D JSONValue). If $(D arg) is a $(D JSONValue)
420 * its value and type will be copied to the new $(D JSONValue).
421 * Note that this is a shallow copy: if type is $(D JSON_TYPE.OBJECT)
422 * or $(D JSON_TYPE.ARRAY) then only the reference to the data will
423 * be copied.
424 * Otherwise, $(D arg) must be implicitly convertible to one of the
425 * following types: $(D typeof(null)), $(D string), $(D ulong),
426 * $(D long), $(D double), an associative array $(D V[K]) for any $(D V)
427 * and $(D K) i.e. a JSON object, any array or $(D bool). The type will
428 * be set accordingly.
430 this(T)(T arg) if (!isStaticArray!T)
432 assign(arg);
434 /// Ditto
435 this(T)(ref T arg) if (isStaticArray!T)
437 assignRef(arg);
439 /// Ditto
440 this(T : JSONValue)(inout T arg) inout
442 store = arg.store;
443 type_tag = arg.type;
446 @safe unittest
448 JSONValue j = JSONValue( "a string" );
449 j = JSONValue(42);
451 j = JSONValue( [1, 2, 3] );
452 assert(j.type == JSON_TYPE.ARRAY);
454 j = JSONValue( ["language": "D"] );
455 assert(j.type == JSON_TYPE.OBJECT);
458 void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue))
460 assign(arg);
463 void opAssign(T)(ref T arg) if (isStaticArray!T)
465 assignRef(arg);
468 /***
469 * Array syntax for json arrays.
470 * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.ARRAY).
472 ref inout(JSONValue) opIndex(size_t i) inout pure @safe
474 auto a = this.arrayNoRef;
475 enforceEx!JSONException(i < a.length,
476 "JSONValue array index is out of range");
477 return a[i];
480 @safe unittest
482 JSONValue j = JSONValue( [42, 43, 44] );
483 assert( j[0].integer == 42 );
484 assert( j[1].integer == 43 );
487 /***
488 * Hash syntax for json objects.
489 * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT).
491 ref inout(JSONValue) opIndex(string k) inout pure @safe
493 auto o = this.objectNoRef;
494 return *enforce!JSONException(k in o,
495 "Key not found: " ~ k);
498 @safe unittest
500 JSONValue j = JSONValue( ["language": "D"] );
501 assert( j["language"].str == "D" );
504 /***
505 * Operator sets $(D value) for element of JSON object by $(D key).
507 * If JSON value is null, then operator initializes it with object and then
508 * sets $(D value) for it.
510 * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT)
511 * or $(D JSON_TYPE.NULL).
513 void opIndexAssign(T)(auto ref T value, string key) pure
515 enforceEx!JSONException(type == JSON_TYPE.OBJECT || type == JSON_TYPE.NULL,
516 "JSONValue must be object or null");
517 JSONValue[string] aa = null;
518 if (type == JSON_TYPE.OBJECT)
520 aa = this.objectNoRef;
523 aa[key] = value;
524 this.object = aa;
527 @safe unittest
529 JSONValue j = JSONValue( ["language": "D"] );
530 j["language"].str = "Perl";
531 assert( j["language"].str == "Perl" );
534 void opIndexAssign(T)(T arg, size_t i) pure
536 auto a = this.arrayNoRef;
537 enforceEx!JSONException(i < a.length,
538 "JSONValue array index is out of range");
539 a[i] = arg;
540 this.array = a;
543 @safe unittest
545 JSONValue j = JSONValue( ["Perl", "C"] );
546 j[1].str = "D";
547 assert( j[1].str == "D" );
550 JSONValue opBinary(string op : "~", T)(T arg) @safe
552 auto a = this.arrayNoRef;
553 static if (isArray!T)
555 return JSONValue(a ~ JSONValue(arg).arrayNoRef);
557 else static if (is(T : JSONValue))
559 return JSONValue(a ~ arg.arrayNoRef);
561 else
563 static assert(false, "argument is not an array or a JSONValue array");
567 void opOpAssign(string op : "~", T)(T arg) @safe
569 auto a = this.arrayNoRef;
570 static if (isArray!T)
572 a ~= JSONValue(arg).arrayNoRef;
574 else static if (is(T : JSONValue))
576 a ~= arg.arrayNoRef;
578 else
580 static assert(false, "argument is not an array or a JSONValue array");
582 this.array = a;
586 * Support for the $(D in) operator.
588 * Tests wether a key can be found in an object.
590 * Returns:
591 * when found, the $(D const(JSONValue)*) that matches to the key,
592 * otherwise $(D null).
594 * Throws: $(D JSONException) if the right hand side argument $(D JSON_TYPE)
595 * is not $(D OBJECT).
597 auto opBinaryRight(string op : "in")(string k) const @safe
599 return k in this.objectNoRef;
602 @safe unittest
604 JSONValue j = [ "language": "D", "author": "walter" ];
605 string a = ("author" in j).str;
608 bool opEquals(const JSONValue rhs) const @nogc nothrow pure @safe
610 return opEquals(rhs);
613 bool opEquals(ref const JSONValue rhs) const @nogc nothrow pure @trusted
615 // Default doesn't work well since store is a union. Compare only
616 // what should be in store.
617 // This is @trusted to remain nogc, nothrow, fast, and usable from @safe code.
618 if (type_tag != rhs.type_tag) return false;
620 final switch (type_tag)
622 case JSON_TYPE.STRING:
623 return store.str == rhs.store.str;
624 case JSON_TYPE.INTEGER:
625 return store.integer == rhs.store.integer;
626 case JSON_TYPE.UINTEGER:
627 return store.uinteger == rhs.store.uinteger;
628 case JSON_TYPE.FLOAT:
629 return store.floating == rhs.store.floating;
630 case JSON_TYPE.OBJECT:
631 return store.object == rhs.store.object;
632 case JSON_TYPE.ARRAY:
633 return store.array == rhs.store.array;
634 case JSON_TYPE.TRUE:
635 case JSON_TYPE.FALSE:
636 case JSON_TYPE.NULL:
637 return true;
641 /// Implements the foreach $(D opApply) interface for json arrays.
642 int opApply(scope int delegate(size_t index, ref JSONValue) dg) @system
644 int result;
646 foreach (size_t index, ref value; array)
648 result = dg(index, value);
649 if (result)
650 break;
653 return result;
656 /// Implements the foreach $(D opApply) interface for json objects.
657 int opApply(scope int delegate(string key, ref JSONValue) dg) @system
659 enforce!JSONException(type == JSON_TYPE.OBJECT,
660 "JSONValue is not an object");
661 int result;
663 foreach (string key, ref value; object)
665 result = dg(key, value);
666 if (result)
667 break;
670 return result;
673 /***
674 * Implicitly calls $(D toJSON) on this JSONValue.
676 * $(I options) can be used to tweak the conversion behavior.
678 string toString(in JSONOptions options = JSONOptions.none) const @safe
680 return toJSON(this, false, options);
683 /***
684 * Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but
685 * also passes $(I true) as $(I pretty) argument.
687 * $(I options) can be used to tweak the conversion behavior
689 string toPrettyString(in JSONOptions options = JSONOptions.none) const @safe
691 return toJSON(this, true, options);
696 Parses a serialized string and returns a tree of JSON values.
697 Throws: $(LREF JSONException) if the depth exceeds the max depth.
698 Params:
699 json = json-formatted string to parse
700 maxDepth = maximum depth of nesting allowed, -1 disables depth checking
701 options = enable decoding string representations of NaN/Inf as float values
703 JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none)
704 if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
706 import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
707 import std.typecons : Yes;
708 JSONValue root;
709 root.type_tag = JSON_TYPE.NULL;
711 // Avoid UTF decoding when possible, as it is unnecessary when
712 // processing JSON.
713 static if (is(T : const(char)[]))
714 alias Char = char;
715 else
716 alias Char = Unqual!(ElementType!T);
718 if (json.empty) return root;
720 int depth = -1;
721 Char next = 0;
722 int line = 1, pos = 0;
724 void error(string msg)
726 throw new JSONException(msg, line, pos);
729 Char popChar()
731 if (json.empty) error("Unexpected end of data.");
732 static if (is(T : const(char)[]))
734 Char c = json[0];
735 json = json[1..$];
737 else
739 Char c = json.front;
740 json.popFront();
743 if (c == '\n')
745 line++;
746 pos = 0;
748 else
750 pos++;
753 return c;
756 Char peekChar()
758 if (!next)
760 if (json.empty) return '\0';
761 next = popChar();
763 return next;
766 void skipWhitespace()
768 while (isWhite(peekChar())) next = 0;
771 Char getChar(bool SkipWhitespace = false)()
773 static if (SkipWhitespace) skipWhitespace();
775 Char c;
776 if (next)
778 c = next;
779 next = 0;
781 else
782 c = popChar();
784 return c;
787 void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
789 static if (SkipWhitespace) skipWhitespace();
790 auto c2 = getChar();
791 static if (!CaseSensitive) c2 = toLower(c2);
793 if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'."));
796 bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
798 static if (SkipWhitespace) skipWhitespace();
799 auto c2 = peekChar();
800 static if (!CaseSensitive) c2 = toLower(c2);
802 if (c2 != c) return false;
804 getChar();
805 return true;
808 wchar parseWChar()
810 wchar val = 0;
811 foreach_reverse (i; 0 .. 4)
813 auto hex = toUpper(getChar());
814 if (!isHexDigit(hex)) error("Expecting hex character");
815 val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i);
817 return val;
820 string parseString()
822 import std.ascii : isControl;
823 import std.uni : isSurrogateHi, isSurrogateLo;
824 import std.utf : encode, decode;
826 auto str = appender!string();
828 Next:
829 switch (peekChar())
831 case '"':
832 getChar();
833 break;
835 case '\\':
836 getChar();
837 auto c = getChar();
838 switch (c)
840 case '"': str.put('"'); break;
841 case '\\': str.put('\\'); break;
842 case '/': str.put('/'); break;
843 case 'b': str.put('\b'); break;
844 case 'f': str.put('\f'); break;
845 case 'n': str.put('\n'); break;
846 case 'r': str.put('\r'); break;
847 case 't': str.put('\t'); break;
848 case 'u':
849 wchar wc = parseWChar();
850 dchar val;
851 // Non-BMP characters are escaped as a pair of
852 // UTF-16 surrogate characters (see RFC 4627).
853 if (isSurrogateHi(wc))
855 wchar[2] pair;
856 pair[0] = wc;
857 if (getChar() != '\\') error("Expected escaped low surrogate after escaped high surrogate");
858 if (getChar() != 'u') error("Expected escaped low surrogate after escaped high surrogate");
859 pair[1] = parseWChar();
860 size_t index = 0;
861 val = decode(pair[], index);
862 if (index != 2) error("Invalid escaped surrogate pair");
864 else
865 if (isSurrogateLo(wc))
866 error(text("Unexpected low surrogate"));
867 else
868 val = wc;
870 char[4] buf;
871 immutable len = encode!(Yes.useReplacementDchar)(buf, val);
872 str.put(buf[0 .. len]);
873 break;
875 default:
876 error(text("Invalid escape sequence '\\", c, "'."));
878 goto Next;
880 default:
881 // RFC 7159 states that control characters U+0000 through
882 // U+001F must not appear unescaped in a JSON string.
883 auto c = getChar();
884 if (isControl(c))
885 error("Illegal control character.");
886 str.put(c);
887 goto Next;
890 return str.data.length ? str.data : "";
893 bool tryGetSpecialFloat(string str, out double val) {
894 switch (str)
896 case JSONFloatLiteral.nan:
897 val = double.nan;
898 return true;
899 case JSONFloatLiteral.inf:
900 val = double.infinity;
901 return true;
902 case JSONFloatLiteral.negativeInf:
903 val = -double.infinity;
904 return true;
905 default:
906 return false;
910 void parseValue(ref JSONValue value)
912 depth++;
914 if (maxDepth != -1 && depth > maxDepth) error("Nesting too deep.");
916 auto c = getChar!true();
918 switch (c)
920 case '{':
921 if (testChar('}'))
923 value.object = null;
924 break;
927 JSONValue[string] obj;
930 checkChar('"');
931 string name = parseString();
932 checkChar(':');
933 JSONValue member;
934 parseValue(member);
935 obj[name] = member;
937 while (testChar(','));
938 value.object = obj;
940 checkChar('}');
941 break;
943 case '[':
944 if (testChar(']'))
946 value.type_tag = JSON_TYPE.ARRAY;
947 break;
950 JSONValue[] arr;
953 JSONValue element;
954 parseValue(element);
955 arr ~= element;
957 while (testChar(','));
959 checkChar(']');
960 value.array = arr;
961 break;
963 case '"':
964 auto str = parseString();
966 // if special float parsing is enabled, check if string represents NaN/Inf
967 if ((options & JSONOptions.specialFloatLiterals) &&
968 tryGetSpecialFloat(str, value.store.floating))
970 // found a special float, its value was placed in value.store.floating
971 value.type_tag = JSON_TYPE.FLOAT;
972 break;
975 value.type_tag = JSON_TYPE.STRING;
976 value.store.str = str;
977 break;
979 case '0': .. case '9':
980 case '-':
981 auto number = appender!string();
982 bool isFloat, isNegative;
984 void readInteger()
986 if (!isDigit(c)) error("Digit expected");
988 Next: number.put(c);
990 if (isDigit(peekChar()))
992 c = getChar();
993 goto Next;
997 if (c == '-')
999 number.put('-');
1000 c = getChar();
1001 isNegative = true;
1004 readInteger();
1006 if (testChar('.'))
1008 isFloat = true;
1009 number.put('.');
1010 c = getChar();
1011 readInteger();
1013 if (testChar!(false, false)('e'))
1015 isFloat = true;
1016 number.put('e');
1017 if (testChar('+')) number.put('+');
1018 else if (testChar('-')) number.put('-');
1019 c = getChar();
1020 readInteger();
1023 string data = number.data;
1024 if (isFloat)
1026 value.type_tag = JSON_TYPE.FLOAT;
1027 value.store.floating = parse!double(data);
1029 else
1031 if (isNegative)
1032 value.store.integer = parse!long(data);
1033 else
1034 value.store.uinteger = parse!ulong(data);
1036 value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ?
1037 JSON_TYPE.UINTEGER : JSON_TYPE.INTEGER;
1039 break;
1041 case 't':
1042 case 'T':
1043 value.type_tag = JSON_TYPE.TRUE;
1044 checkChar!(false, false)('r');
1045 checkChar!(false, false)('u');
1046 checkChar!(false, false)('e');
1047 break;
1049 case 'f':
1050 case 'F':
1051 value.type_tag = JSON_TYPE.FALSE;
1052 checkChar!(false, false)('a');
1053 checkChar!(false, false)('l');
1054 checkChar!(false, false)('s');
1055 checkChar!(false, false)('e');
1056 break;
1058 case 'n':
1059 case 'N':
1060 value.type_tag = JSON_TYPE.NULL;
1061 checkChar!(false, false)('u');
1062 checkChar!(false, false)('l');
1063 checkChar!(false, false)('l');
1064 break;
1066 default:
1067 error(text("Unexpected character '", c, "'."));
1070 depth--;
1073 parseValue(root);
1074 return root;
1077 @safe unittest
1079 enum issue15742objectOfObject = `{ "key1": { "key2": 1 }}`;
1080 static assert(parseJSON(issue15742objectOfObject).type == JSON_TYPE.OBJECT);
1082 enum issue15742arrayOfArray = `[[1]]`;
1083 static assert(parseJSON(issue15742arrayOfArray).type == JSON_TYPE.ARRAY);
1086 @safe unittest
1088 // Ensure we can parse and use JSON from @safe code
1089 auto a = `{ "key1": { "key2": 1 }}`.parseJSON;
1090 assert(a["key1"]["key2"].integer == 1);
1091 assert(a.toString == `{"key1":{"key2":1}}`);
1094 @system unittest
1096 // Ensure we can parse JSON from a @system range.
1097 struct Range
1099 string s;
1100 size_t index;
1101 @system
1103 bool empty() { return index >= s.length; }
1104 void popFront() { index++; }
1105 char front() { return s[index]; }
1108 auto s = Range(`{ "key1": { "key2": 1 }}`);
1109 auto json = parseJSON(s);
1110 assert(json["key1"]["key2"].integer == 1);
1114 Parses a serialized string and returns a tree of JSON values.
1115 Throws: $(REF JSONException, std,json) if the depth exceeds the max depth.
1116 Params:
1117 json = json-formatted string to parse
1118 options = enable decoding string representations of NaN/Inf as float values
1120 JSONValue parseJSON(T)(T json, JSONOptions options)
1121 if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
1123 return parseJSON!T(json, -1, options);
1126 deprecated(
1127 "Please use the overload that takes a ref JSONValue rather than a pointer. This overload will "
1128 ~ "be removed in November 2017.")
1129 string toJSON(in JSONValue* root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe
1131 return toJSON(*root, pretty, options);
1135 Takes a tree of JSON values and returns the serialized string.
1137 Any Object types will be serialized in a key-sorted order.
1139 If $(D pretty) is false no whitespaces are generated.
1140 If $(D pretty) is true serialized string is formatted to be human-readable.
1141 Set the $(LREF JSONOptions.specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings.
1143 string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe
1145 auto json = appender!string();
1147 void toStringImpl(Char)(string str) @safe
1149 json.put('"');
1151 foreach (Char c; str)
1153 switch (c)
1155 case '"': json.put("\\\""); break;
1156 case '\\': json.put("\\\\"); break;
1158 case '/':
1159 if (!(options & JSONOptions.doNotEscapeSlashes))
1160 json.put('\\');
1161 json.put('/');
1162 break;
1164 case '\b': json.put("\\b"); break;
1165 case '\f': json.put("\\f"); break;
1166 case '\n': json.put("\\n"); break;
1167 case '\r': json.put("\\r"); break;
1168 case '\t': json.put("\\t"); break;
1169 default:
1171 import std.ascii : isControl;
1172 import std.utf : encode;
1174 // Make sure we do UTF decoding iff we want to
1175 // escape Unicode characters.
1176 assert(((options & JSONOptions.escapeNonAsciiChars) != 0)
1177 == is(Char == dchar));
1179 with (JSONOptions) if (isControl(c) ||
1180 ((options & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80))
1182 // Ensure non-BMP characters are encoded as a pair
1183 // of UTF-16 surrogate characters, as per RFC 4627.
1184 wchar[2] wchars; // 1 or 2 UTF-16 code units
1185 size_t wNum = encode(wchars, c); // number of UTF-16 code units
1186 foreach (wc; wchars[0 .. wNum])
1188 json.put("\\u");
1189 foreach_reverse (i; 0 .. 4)
1191 char ch = (wc >>> (4 * i)) & 0x0f;
1192 ch += ch < 10 ? '0' : 'A' - 10;
1193 json.put(ch);
1197 else
1199 json.put(c);
1205 json.put('"');
1208 void toString(string str) @safe
1210 // Avoid UTF decoding when possible, as it is unnecessary when
1211 // processing JSON.
1212 if (options & JSONOptions.escapeNonAsciiChars)
1213 toStringImpl!dchar(str);
1214 else
1215 toStringImpl!char(str);
1218 void toValue(ref in JSONValue value, ulong indentLevel) @safe
1220 void putTabs(ulong additionalIndent = 0)
1222 if (pretty)
1223 foreach (i; 0 .. indentLevel + additionalIndent)
1224 json.put(" ");
1226 void putEOL()
1228 if (pretty)
1229 json.put('\n');
1231 void putCharAndEOL(char ch)
1233 json.put(ch);
1234 putEOL();
1237 final switch (value.type)
1239 case JSON_TYPE.OBJECT:
1240 auto obj = value.objectNoRef;
1241 if (!obj.length)
1243 json.put("{}");
1245 else
1247 putCharAndEOL('{');
1248 bool first = true;
1250 void emit(R)(R names)
1252 foreach (name; names)
1254 auto member = obj[name];
1255 if (!first)
1256 putCharAndEOL(',');
1257 first = false;
1258 putTabs(1);
1259 toString(name);
1260 json.put(':');
1261 if (pretty)
1262 json.put(' ');
1263 toValue(member, indentLevel + 1);
1267 import std.algorithm.sorting : sort;
1268 // @@@BUG@@@ 14439
1269 // auto names = obj.keys; // aa.keys can't be called in @safe code
1270 auto names = new string[obj.length];
1271 size_t i = 0;
1272 foreach (k, v; obj)
1274 names[i] = k;
1275 i++;
1277 sort(names);
1278 emit(names);
1280 putEOL();
1281 putTabs();
1282 json.put('}');
1284 break;
1286 case JSON_TYPE.ARRAY:
1287 auto arr = value.arrayNoRef;
1288 if (arr.empty)
1290 json.put("[]");
1292 else
1294 putCharAndEOL('[');
1295 foreach (i, el; arr)
1297 if (i)
1298 putCharAndEOL(',');
1299 putTabs(1);
1300 toValue(el, indentLevel + 1);
1302 putEOL();
1303 putTabs();
1304 json.put(']');
1306 break;
1308 case JSON_TYPE.STRING:
1309 toString(value.str);
1310 break;
1312 case JSON_TYPE.INTEGER:
1313 json.put(to!string(value.store.integer));
1314 break;
1316 case JSON_TYPE.UINTEGER:
1317 json.put(to!string(value.store.uinteger));
1318 break;
1320 case JSON_TYPE.FLOAT:
1321 import std.math : isNaN, isInfinity;
1323 auto val = value.store.floating;
1325 if (val.isNaN)
1327 if (options & JSONOptions.specialFloatLiterals)
1329 toString(JSONFloatLiteral.nan);
1331 else
1333 throw new JSONException(
1334 "Cannot encode NaN. Consider passing the specialFloatLiterals flag.");
1337 else if (val.isInfinity)
1339 if (options & JSONOptions.specialFloatLiterals)
1341 toString((val > 0) ? JSONFloatLiteral.inf : JSONFloatLiteral.negativeInf);
1343 else
1345 throw new JSONException(
1346 "Cannot encode Infinity. Consider passing the specialFloatLiterals flag.");
1349 else
1351 import std.format : format;
1352 // The correct formula for the number of decimal digits needed for lossless round
1353 // trips is actually:
1354 // ceil(log(pow(2.0, double.mant_dig - 1)) / log(10.0) + 1) == (double.dig + 2)
1355 // Anything less will round off (1 + double.epsilon)
1356 json.put("%.18g".format(val));
1358 break;
1360 case JSON_TYPE.TRUE:
1361 json.put("true");
1362 break;
1364 case JSON_TYPE.FALSE:
1365 json.put("false");
1366 break;
1368 case JSON_TYPE.NULL:
1369 json.put("null");
1370 break;
1374 toValue(root, 0);
1375 return json.data;
1378 @safe unittest // bugzilla 12897
1380 JSONValue jv0 = JSONValue("test测试");
1381 assert(toJSON(jv0, false, JSONOptions.escapeNonAsciiChars) == `"test\u6D4B\u8BD5"`);
1382 JSONValue jv00 = JSONValue("test\u6D4B\u8BD5");
1383 assert(toJSON(jv00, false, JSONOptions.none) == `"test测试"`);
1384 assert(toJSON(jv0, false, JSONOptions.none) == `"test测试"`);
1385 JSONValue jv1 = JSONValue("été");
1386 assert(toJSON(jv1, false, JSONOptions.escapeNonAsciiChars) == `"\u00E9t\u00E9"`);
1387 JSONValue jv11 = JSONValue("\u00E9t\u00E9");
1388 assert(toJSON(jv11, false, JSONOptions.none) == `"été"`);
1389 assert(toJSON(jv1, false, JSONOptions.none) == `"été"`);
1393 Exception thrown on JSON errors
1395 class JSONException : Exception
1397 this(string msg, int line = 0, int pos = 0) pure nothrow @safe
1399 if (line)
1400 super(text(msg, " (Line ", line, ":", pos, ")"));
1401 else
1402 super(msg);
1405 this(string msg, string file, size_t line) pure nothrow @safe
1407 super(msg, file, line);
1412 @system unittest
1414 import std.exception;
1415 JSONValue jv = "123";
1416 assert(jv.type == JSON_TYPE.STRING);
1417 assertNotThrown(jv.str);
1418 assertThrown!JSONException(jv.integer);
1419 assertThrown!JSONException(jv.uinteger);
1420 assertThrown!JSONException(jv.floating);
1421 assertThrown!JSONException(jv.object);
1422 assertThrown!JSONException(jv.array);
1423 assertThrown!JSONException(jv["aa"]);
1424 assertThrown!JSONException(jv[2]);
1426 jv = -3;
1427 assert(jv.type == JSON_TYPE.INTEGER);
1428 assertNotThrown(jv.integer);
1430 jv = cast(uint) 3;
1431 assert(jv.type == JSON_TYPE.UINTEGER);
1432 assertNotThrown(jv.uinteger);
1434 jv = 3.0;
1435 assert(jv.type == JSON_TYPE.FLOAT);
1436 assertNotThrown(jv.floating);
1438 jv = ["key" : "value"];
1439 assert(jv.type == JSON_TYPE.OBJECT);
1440 assertNotThrown(jv.object);
1441 assertNotThrown(jv["key"]);
1442 assert("key" in jv);
1443 assert("notAnElement" !in jv);
1444 assertThrown!JSONException(jv["notAnElement"]);
1445 const cjv = jv;
1446 assert("key" in cjv);
1447 assertThrown!JSONException(cjv["notAnElement"]);
1449 foreach (string key, value; jv)
1451 static assert(is(typeof(value) == JSONValue));
1452 assert(key == "key");
1453 assert(value.type == JSON_TYPE.STRING);
1454 assertNotThrown(value.str);
1455 assert(value.str == "value");
1458 jv = [3, 4, 5];
1459 assert(jv.type == JSON_TYPE.ARRAY);
1460 assertNotThrown(jv.array);
1461 assertNotThrown(jv[2]);
1462 foreach (size_t index, value; jv)
1464 static assert(is(typeof(value) == JSONValue));
1465 assert(value.type == JSON_TYPE.INTEGER);
1466 assertNotThrown(value.integer);
1467 assert(index == (value.integer-3));
1470 jv = null;
1471 assert(jv.type == JSON_TYPE.NULL);
1472 assert(jv.isNull);
1473 jv = "foo";
1474 assert(!jv.isNull);
1476 jv = JSONValue("value");
1477 assert(jv.type == JSON_TYPE.STRING);
1478 assert(jv.str == "value");
1480 JSONValue jv2 = JSONValue("value");
1481 assert(jv2.type == JSON_TYPE.STRING);
1482 assert(jv2.str == "value");
1484 JSONValue jv3 = JSONValue("\u001c");
1485 assert(jv3.type == JSON_TYPE.STRING);
1486 assert(jv3.str == "\u001C");
1489 @system unittest
1491 // Bugzilla 11504
1493 JSONValue jv = 1;
1494 assert(jv.type == JSON_TYPE.INTEGER);
1496 jv.str = "123";
1497 assert(jv.type == JSON_TYPE.STRING);
1498 assert(jv.str == "123");
1500 jv.integer = 1;
1501 assert(jv.type == JSON_TYPE.INTEGER);
1502 assert(jv.integer == 1);
1504 jv.uinteger = 2u;
1505 assert(jv.type == JSON_TYPE.UINTEGER);
1506 assert(jv.uinteger == 2u);
1508 jv.floating = 1.5;
1509 assert(jv.type == JSON_TYPE.FLOAT);
1510 assert(jv.floating == 1.5);
1512 jv.object = ["key" : JSONValue("value")];
1513 assert(jv.type == JSON_TYPE.OBJECT);
1514 assert(jv.object == ["key" : JSONValue("value")]);
1516 jv.array = [JSONValue(1), JSONValue(2), JSONValue(3)];
1517 assert(jv.type == JSON_TYPE.ARRAY);
1518 assert(jv.array == [JSONValue(1), JSONValue(2), JSONValue(3)]);
1520 jv = true;
1521 assert(jv.type == JSON_TYPE.TRUE);
1523 jv = false;
1524 assert(jv.type == JSON_TYPE.FALSE);
1526 enum E{True = true}
1527 jv = E.True;
1528 assert(jv.type == JSON_TYPE.TRUE);
1531 @system pure unittest
1533 // Adding new json element via array() / object() directly
1535 JSONValue jarr = JSONValue([10]);
1536 foreach (i; 0 .. 9)
1537 jarr.array ~= JSONValue(i);
1538 assert(jarr.array.length == 10);
1540 JSONValue jobj = JSONValue(["key" : JSONValue("value")]);
1541 foreach (i; 0 .. 9)
1542 jobj.object[text("key", i)] = JSONValue(text("value", i));
1543 assert(jobj.object.length == 10);
1546 @system pure unittest
1548 // Adding new json element without array() / object() access
1550 JSONValue jarr = JSONValue([10]);
1551 foreach (i; 0 .. 9)
1552 jarr ~= [JSONValue(i)];
1553 assert(jarr.array.length == 10);
1555 JSONValue jobj = JSONValue(["key" : JSONValue("value")]);
1556 foreach (i; 0 .. 9)
1557 jobj[text("key", i)] = JSONValue(text("value", i));
1558 assert(jobj.object.length == 10);
1560 // No array alias
1561 auto jarr2 = jarr ~ [1,2,3];
1562 jarr2[0] = 999;
1563 assert(jarr[0] == JSONValue(10));
1566 @system unittest
1568 // @system because JSONValue.array is @system
1569 import std.exception;
1571 // An overly simple test suite, if it can parse a serializated string and
1572 // then use the resulting values tree to generate an identical
1573 // serialization, both the decoder and encoder works.
1575 auto jsons = [
1576 `null`,
1577 `true`,
1578 `false`,
1579 `0`,
1580 `123`,
1581 `-4321`,
1582 `0.25`,
1583 `-0.25`,
1584 `""`,
1585 `"hello\nworld"`,
1586 `"\"\\\/\b\f\n\r\t"`,
1587 `[]`,
1588 `[12,"foo",true,false]`,
1589 `{}`,
1590 `{"a":1,"b":null}`,
1591 `{"goodbye":[true,"or",false,["test",42,{"nested":{"a":23.5,"b":0.140625}}]],`
1592 ~`"hello":{"array":[12,null,{}],"json":"is great"}}`,
1595 enum dbl1_844 = `1.8446744073709568`;
1596 version (MinGW)
1597 jsons ~= dbl1_844 ~ `e+019`;
1598 else
1599 jsons ~= dbl1_844 ~ `e+19`;
1601 JSONValue val;
1602 string result;
1603 foreach (json; jsons)
1607 val = parseJSON(json);
1608 enum pretty = false;
1609 result = toJSON(val, pretty);
1610 assert(result == json, text(result, " should be ", json));
1612 catch (JSONException e)
1614 import std.stdio : writefln;
1615 writefln(text(json, "\n", e.toString()));
1619 // Should be able to correctly interpret unicode entities
1620 val = parseJSON(`"\u003C\u003E"`);
1621 assert(toJSON(val) == "\"\&lt;\&gt;\"");
1622 assert(val.to!string() == "\"\&lt;\&gt;\"");
1623 val = parseJSON(`"\u0391\u0392\u0393"`);
1624 assert(toJSON(val) == "\"\&Alpha;\&Beta;\&Gamma;\"");
1625 assert(val.to!string() == "\"\&Alpha;\&Beta;\&Gamma;\"");
1626 val = parseJSON(`"\u2660\u2666"`);
1627 assert(toJSON(val) == "\"\&spades;\&diams;\"");
1628 assert(val.to!string() == "\"\&spades;\&diams;\"");
1630 //0x7F is a control character (see Unicode spec)
1631 val = parseJSON(`"\u007F"`);
1632 assert(toJSON(val) == "\"\\u007F\"");
1633 assert(val.to!string() == "\"\\u007F\"");
1635 with(parseJSON(`""`))
1636 assert(str == "" && str !is null);
1637 with(parseJSON(`[]`))
1638 assert(!array.length);
1640 // Formatting
1641 val = parseJSON(`{"a":[null,{"x":1},{},[]]}`);
1642 assert(toJSON(val, true) == `{
1643 "a": [
1644 null,
1646 "x": 1
1651 }`);
1654 @safe unittest
1656 auto json = `"hello\nworld"`;
1657 const jv = parseJSON(json);
1658 assert(jv.toString == json);
1659 assert(jv.toPrettyString == json);
1662 @system pure unittest
1664 // Bugzilla 12969
1666 JSONValue jv;
1667 jv["int"] = 123;
1669 assert(jv.type == JSON_TYPE.OBJECT);
1670 assert("int" in jv);
1671 assert(jv["int"].integer == 123);
1673 jv["array"] = [1, 2, 3, 4, 5];
1675 assert(jv["array"].type == JSON_TYPE.ARRAY);
1676 assert(jv["array"][2].integer == 3);
1678 jv["str"] = "D language";
1679 assert(jv["str"].type == JSON_TYPE.STRING);
1680 assert(jv["str"].str == "D language");
1682 jv["bool"] = false;
1683 assert(jv["bool"].type == JSON_TYPE.FALSE);
1685 assert(jv.object.length == 4);
1687 jv = [5, 4, 3, 2, 1];
1688 assert( jv.type == JSON_TYPE.ARRAY );
1689 assert( jv[3].integer == 2 );
1692 @safe unittest
1694 auto s = q"EOF
1699 potato
1701 EOF";
1703 import std.exception;
1705 auto e = collectException!JSONException(parseJSON(s));
1706 assert(e.msg == "Unexpected character 'p'. (Line 5:3)", e.msg);
1709 // handling of special float values (NaN, Inf, -Inf)
1710 @safe unittest
1712 import std.exception : assertThrown;
1713 import std.math : isNaN, isInfinity;
1715 // expected representations of NaN and Inf
1716 enum {
1717 nanString = '"' ~ JSONFloatLiteral.nan ~ '"',
1718 infString = '"' ~ JSONFloatLiteral.inf ~ '"',
1719 negativeInfString = '"' ~ JSONFloatLiteral.negativeInf ~ '"',
1722 // with the specialFloatLiterals option, encode NaN/Inf as strings
1723 assert(JSONValue(float.nan).toString(JSONOptions.specialFloatLiterals) == nanString);
1724 assert(JSONValue(double.infinity).toString(JSONOptions.specialFloatLiterals) == infString);
1725 assert(JSONValue(-real.infinity).toString(JSONOptions.specialFloatLiterals) == negativeInfString);
1727 // without the specialFloatLiterals option, throw on encoding NaN/Inf
1728 assertThrown!JSONException(JSONValue(float.nan).toString);
1729 assertThrown!JSONException(JSONValue(double.infinity).toString);
1730 assertThrown!JSONException(JSONValue(-real.infinity).toString);
1732 // when parsing json with specialFloatLiterals option, decode special strings as floats
1733 JSONValue jvNan = parseJSON(nanString, JSONOptions.specialFloatLiterals);
1734 JSONValue jvInf = parseJSON(infString, JSONOptions.specialFloatLiterals);
1735 JSONValue jvNegInf = parseJSON(negativeInfString, JSONOptions.specialFloatLiterals);
1737 assert(jvNan.floating.isNaN);
1738 assert(jvInf.floating.isInfinity && jvInf.floating > 0);
1739 assert(jvNegInf.floating.isInfinity && jvNegInf.floating < 0);
1741 // when parsing json without the specialFloatLiterals option, decode special strings as strings
1742 jvNan = parseJSON(nanString);
1743 jvInf = parseJSON(infString);
1744 jvNegInf = parseJSON(negativeInfString);
1746 assert(jvNan.str == JSONFloatLiteral.nan);
1747 assert(jvInf.str == JSONFloatLiteral.inf);
1748 assert(jvNegInf.str == JSONFloatLiteral.negativeInf);
1751 pure nothrow @safe @nogc unittest
1753 JSONValue testVal;
1754 testVal = "test";
1755 testVal = 10;
1756 testVal = 10u;
1757 testVal = 1.0;
1758 testVal = (JSONValue[string]).init;
1759 testVal = JSONValue[].init;
1760 testVal = null;
1761 assert(testVal.isNull);
1764 pure nothrow @safe unittest // issue 15884
1766 import std.typecons;
1767 void Test(C)() {
1768 C[] a = ['x'];
1769 JSONValue testVal = a;
1770 assert(testVal.type == JSON_TYPE.STRING);
1771 testVal = a.idup;
1772 assert(testVal.type == JSON_TYPE.STRING);
1774 Test!char();
1775 Test!wchar();
1776 Test!dchar();
1779 @safe unittest // issue 15885
1781 enum bool realInDoublePrecision = real.mant_dig == double.mant_dig;
1783 static bool test(const double num0)
1785 import std.math : feqrel;
1786 const json0 = JSONValue(num0);
1787 const num1 = to!double(toJSON(json0));
1788 static if (realInDoublePrecision)
1789 return feqrel(num1, num0) >= (double.mant_dig - 1);
1790 else
1791 return num1 == num0;
1794 assert(test( 0.23));
1795 assert(test(-0.23));
1796 assert(test(1.223e+24));
1797 assert(test(23.4));
1798 assert(test(0.0012));
1799 assert(test(30738.22));
1801 assert(test(1 + double.epsilon));
1802 assert(test(double.min_normal));
1803 static if (realInDoublePrecision)
1804 assert(test(-double.max / 2));
1805 else
1806 assert(test(-double.max));
1808 const minSub = double.min_normal * double.epsilon;
1809 assert(test(minSub));
1810 assert(test(3*minSub));
1813 @safe unittest // issue 17555
1815 import std.exception : assertThrown;
1817 assertThrown!JSONException(parseJSON("\"a\nb\""));
1820 @safe unittest // issue 17556
1822 auto v = JSONValue("\U0001D11E");
1823 auto j = toJSON(v, false, JSONOptions.escapeNonAsciiChars);
1824 assert(j == `"\uD834\uDD1E"`);
1827 @safe unittest // issue 5904
1829 string s = `"\uD834\uDD1E"`;
1830 auto j = parseJSON(s);
1831 assert(j.str == "\U0001D11E");
1834 @safe unittest // issue 17557
1836 assert(parseJSON("\"\xFF\"").str == "\xFF");
1837 assert(parseJSON("\"\U0001D11E\"").str == "\U0001D11E");
1840 @safe unittest // issue 17553
1842 auto v = JSONValue("\xFF");
1843 assert(toJSON(v) == "\"\xFF\"");
1846 @safe unittest
1848 import std.utf;
1849 assert(parseJSON("\"\xFF\"".byChar).str == "\xFF");
1850 assert(parseJSON("\"\U0001D11E\"".byChar).str == "\U0001D11E");
1853 @safe unittest // JSONOptions.doNotEscapeSlashes (issue 17587)
1855 assert(parseJSON(`"/"`).toString == `"\/"`);
1856 assert(parseJSON(`"\/"`).toString == `"\/"`);
1857 assert(parseJSON(`"/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
1858 assert(parseJSON(`"\/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);