35 case '{':
return parseObject (t, result);
36 case '[':
return parseArray (t, result);
39 return createFail (
"Expected '{' or '['", &t);
64 case 'a': c =
'\a';
break;
65 case 'b': c =
'\b';
break;
66 case 'f': c =
'\f';
break;
67 case 'n': c =
'\n';
break;
68 case 'r': c =
'\r';
break;
69 case 't': c =
'\t';
break;
75 for (
int i = 4; --i >= 0;)
80 return createFail (
"Syntax error in unicode escape sequence");
82 c = (juce_wchar) ((c << 4) +
static_cast<juce_wchar
> (digitValue));
91 return createFail (
"Unexpected end-of-input in string constant");
105 switch (t2.getAndAdvance())
107 case '{': t = t2;
return parseObject (t, result);
108 case '[': t = t2;
return parseArray (t, result);
109 case '"': t = t2;
return parseString (
'"', t, result);
110 case '\'': t = t2;
return parseString (
'\'', t, result);
113 t2 = t2.findEndOfWhitespace();
118 return parseNumber (t, result,
true);
120 case '0':
case '1':
case '2':
case '3':
case '4':
121 case '5':
case '6':
case '7':
case '8':
case '9':
122 return parseNumber (t, result,
false);
125 if (t2.getAndAdvance() ==
'r' && t2.getAndAdvance() ==
'u' && t2.getAndAdvance() ==
'e')
134 if (t2.getAndAdvance() ==
'a' && t2.getAndAdvance() ==
'l' 135 && t2.getAndAdvance() ==
's' && t2.getAndAdvance() ==
'e')
138 result =
var (
false);
144 if (t2.getAndAdvance() ==
'u' && t2.getAndAdvance() ==
'l' && t2.getAndAdvance() ==
'l')
156 return createFail (
"Syntax error", &t);
163 if (location !=
nullptr)
164 m <<
": \"" <<
String (*location, 20) <<
'"';
174 jassert (intValue >= 0 && intValue < 10);
178 auto previousChar = t;
180 auto digit = ((int) c) -
'0';
182 if (isPositiveAndBelow (digit, 10))
184 intValue = intValue * 10 + digit;
188 if (c ==
'e' || c ==
'E' || c ==
'.')
192 result = isNegative ? -asDouble : asDouble;
197 || c ==
',' || c ==
'}' || c ==
']' || c == 0)
203 return createFail (
"Syntax error in number", &oldT);
206 auto correctedValue = isNegative ? -intValue : intValue;
208 if ((intValue >> 31) != 0)
209 result = correctedValue;
211 result = (int) correctedValue;
219 result = resultObject;
220 auto& resultProperties = resultObject->getProperties();
233 return createFail (
"Unexpected end-of-input in object declaration");
238 auto r = parseString (
'"', t, propertyNameVar);
243 const Identifier propertyName (propertyNameVar.toString());
245 if (propertyName.isValid())
253 return createFail (
"Expected ':', but found", &oldT);
255 resultProperties.set (propertyName,
var());
256 var* propertyValue = resultProperties.getVarPointer (propertyName);
258 auto r2 = parseAny (t, *propertyValue);
276 return createFail (
"Expected object member declaration, but found", &oldT);
285 auto* destArray = result.
getArray();
298 return createFail (
"Unexpected end-of-input in array declaration");
301 destArray->add (
var());
302 auto r = parseAny (t, destArray->getReference (destArray->size() - 1));
318 return createFail (
"Expected object array item, but found", &oldT);
329 int indentLevel,
bool allOnOneLine,
int maximumDecimalPlaces)
341 else if (v.isUndefined())
347 out << (static_cast<bool> (v) ?
"true" :
"false");
349 else if (v.isDouble())
351 auto d =
static_cast<double> (v);
353 if (juce_isfinite (d))
355 out << serialiseDouble (d);
362 else if (v.isArray())
364 writeArray (out, *v.
getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces);
366 else if (v.isObject())
368 if (
auto*
object = v.getDynamicObject())
369 object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces);
376 jassert (! (v.isMethod() || v.isBinaryData()));
382 static void writeEscapedChar (
OutputStream& out,
const unsigned short value)
397 case '\"': out <<
"\\\"";
break;
398 case '\\': out <<
"\\\\";
break;
399 case '\a': out <<
"\\a";
break;
400 case '\b': out <<
"\\b";
break;
401 case '\f': out <<
"\\f";
break;
402 case '\t': out <<
"\\t";
break;
403 case '\r': out <<
"\\r";
break;
404 case '\n': out <<
"\\n";
break;
407 if (c >= 32 && c < 127)
415 CharPointer_UTF16::CharType chars[2];
419 for (
int i = 0; i < 2; ++i)
420 writeEscapedChar (out, (
unsigned short) chars[i]);
424 writeEscapedChar (out, (
unsigned short) c);
433 static void writeSpaces (
OutputStream& out,
int numSpaces)
439 int indentLevel,
bool allOnOneLine,
int maximumDecimalPlaces)
448 for (
int i = 0; i < array.
size(); ++i)
451 writeSpaces (out, indentLevel + indentSize);
453 write (out, array.
getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces);
455 if (i < array.
size() - 1)
460 out <<
',' << newLine;
462 else if (! allOnOneLine)
467 writeSpaces (out, indentLevel);
473 enum { indentSize = 2 };
481 if (! parse (text, result))
491 if (! JSONParser::parseAny (text.
text, result))
509 return JSONParser::parseObjectOrArray (text.
getCharPointer(), result);
515 JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces);
521 JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces);
527 JSONFormatter::writeString (mo, s.
text);
535 if (quote ==
'"' || quote ==
'\'')
536 return JSONParser::parseString (quote, t, result);
548 JSONTests() :
UnitTest (
"JSON",
"JSON") {}
552 juce_wchar buffer[40] = { 0 };
554 for (
int i = 0; i < numElementsInArray (buffer) - 1; ++i)
560 buffer[i] = (juce_wchar) (1 + r.
nextInt (0x10ffff - 1));
565 buffer[i] = (juce_wchar) (1 + r.
nextInt (0xff));
573 char buffer[30] = { 0 };
575 for (
int i = 0; i < numElementsInArray (buffer) - 1; ++i)
577 static const char chars[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
578 buffer[i] = chars [r.
nextInt (
sizeof (chars) - 1)];
586 static var createRandomDouble (
Random& r)
591 static var createRandomVar (
Random& r,
int depth)
593 switch (r.
nextInt (depth > 3 ? 6 : 8))
599 case 4:
return createRandomDouble (r);
600 case 5:
return createRandomWideCharString (r);
604 var v (createRandomVar (r, depth + 1));
606 for (
int i = 1 + r.
nextInt (30); --i >= 0;)
607 v.
append (createRandomVar (r, depth + 1));
616 for (
int i = r.
nextInt (30); --i >= 0;)
617 o->
setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1));
627 void runTest()
override 638 expect (
JSON::parse (
"[ 12345678901234 ]")[0].isInt64());
639 expect (
JSON::parse (
"[ 1.123e3 ]")[0].isDouble());
641 expect (
JSON::parse (
"[-12345678901234]")[0].isInt64());
644 for (
int i = 100; --i >= 0;)
649 v = createRandomVar (r, 0);
655 expect (asString.
isNotEmpty() && parsedString == asString);
660 beginTest (
"Float formatting");
662 std::map<double, String> tests;
665 tests[1.01] =
"1.01";
666 tests[0.76378] =
"0.76378";
667 tests[-10] =
"-10.0";
668 tests[10.01] =
"10.01";
669 tests[0.0123] =
"0.0123";
670 tests[-3.7e-27] =
"-3.7e-27";
671 tests[1e+40] =
"1.0e40";
672 tests[-12345678901234567.0] =
"-1.234567890123457e16";
673 tests[192000] =
"192000.0";
674 tests[1234567] =
"1.234567e6";
675 tests[0.00006] =
"0.00006";
676 tests[0.000006] =
"6.0e-6";
678 for (
auto& test : tests)
684 static JSONTests JSONUnitTests;
Wraps a pointer to a null-terminated ASCII character string, and provides various methods to operate ...
static String toString(const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
Returns a string which contains a JSON-formatted representation of the var object.
static String escapeString(StringRef)
Returns a version of a string with any extended characters escaped.
static Result ok() noexcept
Creates and returns a 'successful' result.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Represents a string identifier, designed for accessing properties by name.
int nextInt() noexcept
Returns the next random 32 bit integer.
A simple class for holding temporary references to a string literal or String.
A variant class, that can be used to hold a range of primitive values.
bool nextBool() noexcept
Returns the next random boolean value.
static bool canRepresent(juce_wchar character) noexcept
Returns true if the given unicode character can be represented in this encoding.
static double readDoubleValue(CharPointerType &text) noexcept
Parses a character string to read a floating-point number.
static void writeToStream(OutputStream &output, const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
Writes a JSON-formatted representation of the var object to the given stream.
static var fromString(StringRef)
Parses a string that was created with the toString() method.
static bool isDigit(char character) noexcept
Checks whether a character is a digit.
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
virtual void setProperty(const Identifier &propertyName, const var &newValue)
Sets a named property.
static Result parse(const String &text, var &parsedResult)
Parses a string of JSON-formatted text, and returns a result code containing any parse errors...
int64 nextInt64() noexcept
Returns the next 64-bit random number.
static bool isWhitespace(char character) noexcept
Checks whether a character is whitespace.
CharPointer_UTF8 findEndOfWhitespace() const noexcept
Returns the first non-whitespace character in the string.
This is a base class for classes that perform a unit test.
static Result fail(const String &errorMessage) noexcept
Creates a 'failure' result.
Represents a dynamically implemented object.
static Result parseQuotedString(String::CharPointerType &text, var &result)
Parses a quoted string-literal in JSON format, returning the un-escaped result in the result paramete...
Wraps a pointer to a null-terminated UTF-32 character string, and provides various methods to operate...
String loadFileAsString() const
Reads a file into memory as a string.
double nextDouble() noexcept
Returns the next random floating-point number.
void write(juce_wchar charToWrite) noexcept
Writes a unicode character to this string, and advances this pointer to point to the next position...
static int getHexDigitValue(juce_wchar digit) noexcept
Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit.
juce_wchar getAndAdvance() noexcept
Returns the character that this pointer is currently pointing to, and then advances the pointer to po...
static String toHexString(IntegerType number)
Returns a string representing this numeric value in hexadecimal.
Represents the 'success' or 'failure' of an operation, and holds an associated error message to descr...
Array< var > * getArray() const noexcept
If this variant holds an array, this provides access to it.
Represents a local file or directory.
The base class for streams that write data to some kind of destination.
Holds a resizable array of primitive or copy-by-value objects.
String paddedLeft(juce_wchar padCharacter, int minimumLength) const
Returns a copy of this string with the specified character repeatedly added to its beginning until th...
int size() const noexcept
Returns the current number of elements in the array.
String::CharPointerType text
The text that is referenced.
static size_t getBytesRequiredFor(juce_wchar charToWrite) noexcept
Returns the number of bytes that would be needed to represent the given unicode character in this enc...
void append(const var &valueToAppend)
Appends an element to the var, converting it to an array if it isn't already one. ...
ElementType & getReference(int index) const noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in...
virtual bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat)
Writes a byte to the output stream a given number of times.
Wraps a pointer to a null-terminated UTF-16 character string, and provides various methods to operate...
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
A random number generator.
bool appendUTF8Char(juce_wchar character)
Appends the utf-8 bytes for a unicode character.
String toUTF8() const
Returns a String created from the (UTF8) data that has been written to the stream.
Writes data to an internal memory buffer, which grows as required.
String toString() const
Attempts to detect the encoding of the data and convert it to a string.
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...