1 // Written in the D programming language.
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)
22 import std
.range
.primitives
;
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);
40 if (const(JSONValue
)* code
= "code" in j
)
42 if (code
.type() == JSON_TYPE
.INTEGER
)
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
);
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
72 Flags that control how json is encoded and parsed.
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 ('/')
87 /// Indicates the type of a $(D JSONValue).
104 import std
.exception
: enforceEx
, enforce
;
112 JSONValue
[string
] object
;
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
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
);
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");
146 @property string
str(string v
) pure nothrow @nogc @safe
154 JSONValue j
= [ "language": "D" ];
157 assert(j
["language"].str == "D");
159 // change existing key to new string
160 j
["language"].str = "Perl";
161 assert(j
["language"].str == "Perl");
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
;
176 @property long integer(long v
) pure nothrow @safe @nogc
179 return store
.integer
;
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
;
194 @property ulong uinteger(ulong v
) pure nothrow @safe @nogc
197 return store
.uinteger
;
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
;
213 @property double floating(double v
) pure nothrow @safe @nogc
216 return store
.floating
;
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");
237 @property JSONValue
[string
] object(JSONValue
[string
] v
) pure nothrow @nogc @safe
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:
250 * json.object = null;
251 * json.objectNoRef["hello"] = JSONValue("world");
252 * assert("hello" !in json.object);
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");
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");
283 @property JSONValue
[] array(JSONValue
[] v
) pure nothrow @nogc @safe
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
297 * json.array = [JSONValue("hello")];
298 * json.arrayNoRef ~= JSONValue("world");
299 * assert(json.array.length == 1);
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");
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
;
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'
335 import std
.utf
: byUTF
;
336 store
.str = cast(immutable)(arg
.byUTF
!char.array
);
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
;
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
; }();
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
))
381 () @trusted { store
.array
= t
; }();
385 JSONValue
[] new_arg
= new JSONValue
[arg
.length
];
387 new_arg
[i
] = JSONValue(e
);
388 () @trusted { store
.array
= new_arg
; }();
391 else static if (is(T
: JSONValue
))
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
))
411 JSONValue
[] new_arg
= new JSONValue
[arg
.length
];
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
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
)
435 this(T
)(ref T arg
) if (isStaticArray
!T
)
440 this(T
: JSONValue
)(inout T arg
) inout
448 JSONValue j
= JSONValue( "a string" );
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
))
463 void opAssign(T
)(ref T arg
) if (isStaticArray
!T
)
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");
482 JSONValue j
= JSONValue( [42, 43, 44] );
483 assert( j
[0].integer
== 42 );
484 assert( j
[1].integer
== 43 );
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
);
500 JSONValue j
= JSONValue( ["language": "D"] );
501 assert( j
["language"].str == "D" );
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
;
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");
545 JSONValue j
= JSONValue( ["Perl", "C"] );
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
);
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
))
580 static assert(false, "argument is not an array or a JSONValue array");
586 * Support for the $(D in) operator.
588 * Tests wether a key can be found in an object.
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
;
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
;
635 case JSON_TYPE
.FALSE
:
641 /// Implements the foreach $(D opApply) interface for json arrays.
642 int opApply(scope int delegate(size_t index
, ref JSONValue
) dg
) @system
646 foreach (size_t index
, ref value
; array
)
648 result
= dg(index
, value
);
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");
663 foreach (string key
, ref value
; object
)
665 result
= dg(key
, value
);
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
);
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.
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
;
709 root
.type_tag
= JSON_TYPE
.NULL
;
711 // Avoid UTF decoding when possible, as it is unnecessary when
713 static if (is(T
: const(char)[]))
716 alias Char
= Unqual
!(ElementType
!T
);
718 if (json
.empty
) return root
;
722 int line
= 1, pos
= 0;
724 void error(string msg
)
726 throw new JSONException(msg
, line
, pos
);
731 if (json
.empty
) error("Unexpected end of data.");
732 static if (is(T
: const(char)[]))
760 if (json
.empty
) return '\0';
766 void skipWhitespace()
768 while (isWhite(peekChar())) next
= 0;
771 Char
getChar(bool SkipWhitespace
= false)()
773 static if (SkipWhitespace
) skipWhitespace();
787 void checkChar(bool SkipWhitespace
= true, bool CaseSensitive
= true)(char c
)
789 static if (SkipWhitespace
) skipWhitespace();
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;
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
);
822 import std
.ascii
: isControl
;
823 import std
.uni
: isSurrogateHi
, isSurrogateLo
;
824 import std
.utf
: encode
, decode
;
826 auto str = appender
!string();
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;
849 wchar wc
= parseWChar();
851 // Non-BMP characters are escaped as a pair of
852 // UTF-16 surrogate characters (see RFC 4627).
853 if (isSurrogateHi(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();
861 val
= decode(pair
[], index
);
862 if (index
!= 2) error("Invalid escaped surrogate pair");
865 if (isSurrogateLo(wc
))
866 error(text("Unexpected low surrogate"));
871 immutable len
= encode
!(Yes
.useReplacementDchar
)(buf
, val
);
872 str.put(buf
[0 .. len
]);
876 error(text("Invalid escape sequence '\\", c
, "'."));
881 // RFC 7159 states that control characters U+0000 through
882 // U+001F must not appear unescaped in a JSON string.
885 error("Illegal control character.");
890 return str.data
.length ?
str.data
: "";
893 bool tryGetSpecialFloat(string
str, out double val
) {
896 case JSONFloatLiteral
.nan
:
899 case JSONFloatLiteral
.inf
:
900 val
= double.infinity
;
902 case JSONFloatLiteral
.negativeInf
:
903 val
= -double.infinity
;
910 void parseValue(ref JSONValue value
)
914 if (maxDepth
!= -1 && depth
> maxDepth
) error("Nesting too deep.");
916 auto c
= getChar
!true();
927 JSONValue
[string
] obj
;
931 string name
= parseString();
937 while (testChar(','));
946 value
.type_tag
= JSON_TYPE
.ARRAY
;
957 while (testChar(','));
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
;
975 value
.type_tag
= JSON_TYPE
.STRING
;
976 value
.store
.str = str;
979 case '0': .. case '9':
981 auto number
= appender
!string();
982 bool isFloat
, isNegative
;
986 if (!isDigit(c
)) error("Digit expected");
990 if (isDigit(peekChar()))
1013 if (testChar
!(false, false)('e'))
1017 if (testChar('+')) number
.put('+');
1018 else if (testChar('-')) number
.put('-');
1023 string data
= number
.data
;
1026 value
.type_tag
= JSON_TYPE
.FLOAT
;
1027 value
.store
.floating
= parse
!double(data
);
1032 value
.store
.integer
= parse
!long(data
);
1034 value
.store
.uinteger
= parse
!ulong(data
);
1036 value
.type_tag
= !isNegative
&& value
.store
.uinteger
& (1UL << 63) ?
1037 JSON_TYPE
.UINTEGER
: JSON_TYPE
.INTEGER
;
1043 value
.type_tag
= JSON_TYPE
.TRUE
;
1044 checkChar
!(false, false)('r');
1045 checkChar
!(false, false)('u');
1046 checkChar
!(false, false)('e');
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');
1060 value
.type_tag
= JSON_TYPE
.NULL
;
1061 checkChar
!(false, false)('u');
1062 checkChar
!(false, false)('l');
1063 checkChar
!(false, false)('l');
1067 error(text("Unexpected character '", c
, "'."));
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
);
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}}`);
1096 // Ensure we can parse JSON from a @system range.
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.
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
);
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
1151 foreach (Char c
; str)
1155 case '"': json
.put("\\\""); break;
1156 case '\\': json
.put("\\\\"); break;
1159 if (!(options
& JSONOptions
.doNotEscapeSlashes
))
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;
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
])
1189 foreach_reverse (i
; 0 .. 4)
1191 char ch
= (wc
>>> (4 * i
)) & 0x0f;
1192 ch
+= ch
< 10 ?
'0' : 'A' - 10;
1208 void toString(string
str) @safe
1210 // Avoid UTF decoding when possible, as it is unnecessary when
1212 if (options
& JSONOptions
.escapeNonAsciiChars
)
1213 toStringImpl
!dchar(str);
1215 toStringImpl
!char(str);
1218 void toValue(ref in JSONValue value
, ulong indentLevel
) @safe
1220 void putTabs(ulong additionalIndent
= 0)
1223 foreach (i
; 0 .. indentLevel
+ additionalIndent
)
1231 void putCharAndEOL(char ch
)
1237 final switch (value
.type
)
1239 case JSON_TYPE
.OBJECT
:
1240 auto obj
= value
.objectNoRef
;
1250 void emit(R
)(R names
)
1252 foreach (name
; names
)
1254 auto member
= obj
[name
];
1263 toValue(member
, indentLevel
+ 1);
1267 import std
.algorithm
.sorting
: sort
;
1269 // auto names = obj.keys; // aa.keys can't be called in @safe code
1270 auto names
= new string
[obj
.length
];
1286 case JSON_TYPE
.ARRAY
:
1287 auto arr
= value
.arrayNoRef
;
1295 foreach (i
, el
; arr
)
1300 toValue(el
, indentLevel
+ 1);
1308 case JSON_TYPE
.STRING
:
1309 toString(value
.str);
1312 case JSON_TYPE
.INTEGER
:
1313 json
.put(to
!string(value
.store
.integer
));
1316 case JSON_TYPE
.UINTEGER
:
1317 json
.put(to
!string(value
.store
.uinteger
));
1320 case JSON_TYPE
.FLOAT
:
1321 import std
.math
: isNaN
, isInfinity
;
1323 auto val
= value
.store
.floating
;
1327 if (options
& JSONOptions
.specialFloatLiterals
)
1329 toString(JSONFloatLiteral
.nan
);
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
);
1345 throw new JSONException(
1346 "Cannot encode Infinity. Consider passing the specialFloatLiterals flag.");
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
));
1360 case JSON_TYPE
.TRUE
:
1364 case JSON_TYPE
.FALSE
:
1368 case JSON_TYPE
.NULL
:
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
1400 super(text(msg
, " (Line ", line
, ":", pos
, ")"));
1405 this(string msg
, string file
, size_t line
) pure nothrow @safe
1407 super(msg
, file
, line
);
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]);
1427 assert(jv
.type
== JSON_TYPE
.INTEGER
);
1428 assertNotThrown(jv
.integer
);
1431 assert(jv
.type
== JSON_TYPE
.UINTEGER
);
1432 assertNotThrown(jv
.uinteger
);
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"]);
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");
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));
1471 assert(jv
.type
== JSON_TYPE
.NULL
);
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");
1494 assert(jv
.type
== JSON_TYPE
.INTEGER
);
1497 assert(jv
.type
== JSON_TYPE
.STRING
);
1498 assert(jv
.str == "123");
1501 assert(jv
.type
== JSON_TYPE
.INTEGER
);
1502 assert(jv
.integer
== 1);
1505 assert(jv
.type
== JSON_TYPE
.UINTEGER
);
1506 assert(jv
.uinteger
== 2u);
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)]);
1521 assert(jv
.type
== JSON_TYPE
.TRUE
);
1524 assert(jv
.type
== JSON_TYPE
.FALSE
);
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]);
1537 jarr
.array
~= JSONValue(i
);
1538 assert(jarr
.array
.length
== 10);
1540 JSONValue jobj
= JSONValue(["key" : JSONValue("value")]);
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]);
1552 jarr
~= [JSONValue(i
)];
1553 assert(jarr
.array
.length
== 10);
1555 JSONValue jobj
= JSONValue(["key" : JSONValue("value")]);
1557 jobj
[text("key", i
)] = JSONValue(text("value", i
));
1558 assert(jobj
.object
.length
== 10);
1561 auto jarr2
= jarr
~ [1,2,3];
1563 assert(jarr
[0] == JSONValue(10));
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.
1586 `"\"\\\/\b\f\n\r\t"`,
1588 `[12,"foo",true,false]`,
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`;
1597 jsons
~= dbl1_844
~ `e+019`;
1599 jsons
~= dbl1_844
~ `e+19`;
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
) == "\"\<\>\"");
1622 assert(val
.to
!string() == "\"\<\>\"");
1623 val
= parseJSON(`"\u0391\u0392\u0393"`);
1624 assert(toJSON(val
) == "\"\Α\Β\Γ\"");
1625 assert(val
.to
!string() == "\"\Α\Β\Γ\"");
1626 val
= parseJSON(`"\u2660\u2666"`);
1627 assert(toJSON(val
) == "\"\♠\♦\"");
1628 assert(val
.to
!string() == "\"\♠\♦\"");
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
);
1641 val
= parseJSON(`{"a":[null,{"x":1},{},[]]}`);
1642 assert(toJSON(val
, true) == `{
1656 auto json
= `"hello\nworld"`;
1657 const jv
= parseJSON(json
);
1658 assert(jv
.toString
== json
);
1659 assert(jv
.toPrettyString
== json
);
1662 @system pure unittest
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");
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 );
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)
1712 import std
.exception
: assertThrown
;
1713 import std
.math
: isNaN
, isInfinity
;
1715 // expected representations of NaN and Inf
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
1758 testVal
= (JSONValue
[string
]).init
;
1759 testVal
= JSONValue
[].init
;
1761 assert(testVal
.isNull
);
1764 pure nothrow @safe unittest // issue 15884
1766 import std
.typecons
;
1769 JSONValue testVal
= a
;
1770 assert(testVal
.type
== JSON_TYPE
.STRING
);
1772 assert(testVal
.type
== JSON_TYPE
.STRING
);
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);
1791 return num1
== num0
;
1794 assert(test( 0.23));
1795 assert(test(-0.23));
1796 assert(test(1.223e+24));
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));
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\"");
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
) == `"/"`);