LMMS
Loading...
Searching...
No Matches
juce_JSON.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
27{
28 JSONParser (String::CharPointerType text) : startLocation (text), currentLocation (text) {}
29
30 String::CharPointerType startLocation, currentLocation;
31
33 {
35 int line = 1, column = 1;
36
37 String getDescription() const { return String (line) + ":" + String (column) + ": error: " + message; }
39 };
40
41 [[noreturn]] void throwError (juce::String message, String::CharPointerType location)
42 {
44 e.message = std::move (message);
45
46 for (auto i = startLocation; i < location && ! i.isEmpty(); ++i)
47 {
48 ++e.column;
49 if (*i == '\n') { e.column = 1; e.line++; }
50 }
51
52 throw e;
53 }
54
55 void skipWhitespace() { currentLocation = currentLocation.findEndOfWhitespace(); }
56 juce_wchar readChar() { return currentLocation.getAndAdvance(); }
57 juce_wchar peekChar() const { return *currentLocation; }
58 bool matchIf (char c) { if (peekChar() == (juce_wchar) c) { ++currentLocation; return true; } return false; }
59 bool isEOF() const { return peekChar() == 0; }
60
61 bool matchString (const char* t)
62 {
63 while (*t != 0)
64 if (! matchIf (*t++))
65 return false;
66
67 return true;
68 }
69
71 {
73
74 if (matchIf ('{')) return parseObject();
75 if (matchIf ('[')) return parseArray();
76
77 if (! isEOF())
78 throwError ("Expected '{' or '['", currentLocation);
79
80 return {};
81 }
82
83 String parseString (const juce_wchar quoteChar)
84 {
85 MemoryOutputStream buffer (256);
86
87 for (;;)
88 {
89 auto c = readChar();
90
91 if (c == quoteChar)
92 break;
93
94 if (c == '\\')
95 {
96 auto errorLocation = currentLocation;
97 c = readChar();
98
99 switch (c)
100 {
101 case '"':
102 case '\'':
103 case '\\':
104 case '/': break;
105
106 case 'a': c = '\a'; break;
107 case 'b': c = '\b'; break;
108 case 'f': c = '\f'; break;
109 case 'n': c = '\n'; break;
110 case 'r': c = '\r'; break;
111 case 't': c = '\t'; break;
112
113 case 'u':
114 {
115 c = 0;
116
117 for (int i = 4; --i >= 0;)
118 {
120
121 if (digitValue < 0)
122 throwError ("Syntax error in unicode escape sequence", errorLocation);
123
124 c = (juce_wchar) ((c << 4) + static_cast<juce_wchar> (digitValue));
125 }
126
127 break;
128 }
129
130 default: break;
131 }
132 }
133
134 if (c == 0)
135 throwError ("Unexpected EOF in string constant", currentLocation);
136
137 buffer.appendUTF8Char (c);
138 }
139
140 return buffer.toUTF8();
141 }
142
144 {
146 auto originalLocation = currentLocation;
147
148 switch (readChar())
149 {
150 case '{': return parseObject();
151 case '[': return parseArray();
152 case '"': return parseString ('"');
153 case '\'': return parseString ('\'');
154
155 case '-':
157 return parseNumber (true);
158
159 case '0': case '1': case '2': case '3': case '4':
160 case '5': case '6': case '7': case '8': case '9':
161 currentLocation = originalLocation;
162 return parseNumber (false);
163
164 case 't': // "true"
165 if (matchString ("rue"))
166 return var (true);
167
168 break;
169
170 case 'f': // "false"
171 if (matchString ("alse"))
172 return var (false);
173
174 break;
175
176 case 'n': // "null"
177 if (matchString ("ull"))
178 return {};
179
180 break;
181
182 default:
183 break;
184 }
185
186 throwError ("Syntax error", originalLocation);
187 }
188
189 var parseNumber (bool isNegative)
190 {
191 auto originalPos = currentLocation;
192
193 int64 intValue = readChar() - '0';
194 jassert (intValue >= 0 && intValue < 10);
195
196 for (;;)
197 {
198 auto lastPos = currentLocation;
199 auto c = readChar();
200 auto digit = ((int) c) - '0';
201
202 if (isPositiveAndBelow (digit, 10))
203 {
204 intValue = intValue * 10 + digit;
205 continue;
206 }
207
208 if (c == 'e' || c == 'E' || c == '.')
209 {
210 currentLocation = originalPos;
212 return var (isNegative ? -asDouble : asDouble);
213 }
214
216 || c == ',' || c == '}' || c == ']' || c == 0)
217 {
218 currentLocation = lastPos;
219 break;
220 }
221
222 throwError ("Syntax error in number", lastPos);
223 }
224
225 auto correctedValue = isNegative ? -intValue : intValue;
226
227 return (intValue >> 31) != 0 ? var (correctedValue)
228 : var ((int) correctedValue);
229 }
230
232 {
233 auto resultObject = new DynamicObject();
234 var result (resultObject);
235 auto& resultProperties = resultObject->getProperties();
236 auto startOfObjectDecl = currentLocation;
237
238 for (;;)
239 {
241 auto errorLocation = currentLocation;
242 auto c = readChar();
243
244 if (c == '}')
245 break;
246
247 if (c == 0)
248 throwError ("Unexpected EOF in object declaration", startOfObjectDecl);
249
250 if (c != '"')
251 throwError ("Expected a property name in double-quotes", errorLocation);
252
253 errorLocation = currentLocation;
254 Identifier propertyName (parseString ('"'));
255
256 if (! propertyName.isValid())
257 throwError ("Invalid property name", errorLocation);
258
260 errorLocation = currentLocation;
261
262 if (readChar() != ':')
263 throwError ("Expected ':'", errorLocation);
264
265 resultProperties.set (propertyName, parseAny());
266
268 if (matchIf (',')) continue;
269 if (matchIf ('}')) break;
270
271 throwError ("Expected ',' or '}'", currentLocation);
272 }
273
274 return result;
275 }
276
278 {
279 auto result = var (Array<var>());
280 auto destArray = result.getArray();
281 auto startOfArrayDecl = currentLocation;
282
283 for (;;)
284 {
286
287 if (matchIf (']'))
288 break;
289
290 if (isEOF())
291 throwError ("Unexpected EOF in array declaration", startOfArrayDecl);
292
293 destArray->add (parseAny());
295
296 if (matchIf (',')) continue;
297 if (matchIf (']')) break;
298
299 throwError ("Expected ',' or ']'", currentLocation);
300 }
301
302 return result;
303 }
304};
305
306//==============================================================================
308{
309 static void write (OutputStream& out, const var& v,
310 int indentLevel, bool allOnOneLine, int maximumDecimalPlaces)
311 {
312 if (v.isString())
313 {
314 out << '"';
315 writeString (out, v.toString().getCharPointer());
316 out << '"';
317 }
318 else if (v.isVoid())
319 {
320 out << "null";
321 }
322 else if (v.isUndefined())
323 {
324 out << "undefined";
325 }
326 else if (v.isBool())
327 {
328 out << (static_cast<bool> (v) ? "true" : "false");
329 }
330 else if (v.isDouble())
331 {
332 auto d = static_cast<double> (v);
333
334 if (juce_isfinite (d))
335 {
336 out << serialiseDouble (d);
337 }
338 else
339 {
340 out << "null";
341 }
342 }
343 else if (v.isArray())
344 {
345 writeArray (out, *v.getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces);
346 }
347 else if (v.isObject())
348 {
349 if (auto* object = v.getDynamicObject())
350 object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces);
351 else
352 jassertfalse; // Only DynamicObjects can be converted to JSON!
353 }
354 else
355 {
356 // Can't convert these other types of object to JSON!
357 jassert (! (v.isMethod() || v.isBinaryData()));
358
359 out << v.toString();
360 }
361 }
362
363 static void writeEscapedChar (OutputStream& out, const unsigned short value)
364 {
365 out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4);
366 }
367
368 static void writeString (OutputStream& out, String::CharPointerType t)
369 {
370 for (;;)
371 {
372 auto c = t.getAndAdvance();
373
374 switch (c)
375 {
376 case 0: return;
377
378 case '\"': out << "\\\""; break;
379 case '\\': out << "\\\\"; break;
380 case '\a': out << "\\a"; break;
381 case '\b': out << "\\b"; break;
382 case '\f': out << "\\f"; break;
383 case '\t': out << "\\t"; break;
384 case '\r': out << "\\r"; break;
385 case '\n': out << "\\n"; break;
386
387 default:
388 if (c >= 32 && c < 127)
389 {
390 out << (char) c;
391 }
392 else
393 {
395 {
397 CharPointer_UTF16 utf16 (chars);
398 utf16.write (c);
399
400 for (int i = 0; i < 2; ++i)
401 writeEscapedChar (out, (unsigned short) chars[i]);
402 }
403 else
404 {
405 writeEscapedChar (out, (unsigned short) c);
406 }
407 }
408
409 break;
410 }
411 }
412 }
413
414 static void writeSpaces (OutputStream& out, int numSpaces)
415 {
416 out.writeRepeatedByte (' ', (size_t) numSpaces);
417 }
418
419 static void writeArray (OutputStream& out, const Array<var>& array,
420 int indentLevel, bool allOnOneLine, int maximumDecimalPlaces)
421 {
422 out << '[';
423
424 if (! array.isEmpty())
425 {
426 if (! allOnOneLine)
427 out << newLine;
428
429 for (int i = 0; i < array.size(); ++i)
430 {
431 if (! allOnOneLine)
432 writeSpaces (out, indentLevel + indentSize);
433
434 write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces);
435
436 if (i < array.size() - 1)
437 {
438 if (allOnOneLine)
439 out << ", ";
440 else
441 out << ',' << newLine;
442 }
443 else if (! allOnOneLine)
444 out << newLine;
445 }
446
447 if (! allOnOneLine)
448 writeSpaces (out, indentLevel);
449 }
450
451 out << ']';
452 }
453
454 enum { indentSize = 2 };
455};
456
457//==============================================================================
459{
460 var result;
461
462 if (parse (text, result))
463 return result;
464
465 return {};
466}
467
469{
470 try
471 {
472 return JSONParser (text.text).parseAny();
473 }
474 catch (const JSONParser::ErrorException&) {}
475
476 return {};
477}
478
480{
481 return parse (input.readEntireStreamAsString());
482}
483
485{
486 return parse (file.loadFileAsString());
487}
488
490{
491 try
492 {
493 result = JSONParser (text.getCharPointer()).parseObjectOrArray();
494 }
495 catch (const JSONParser::ErrorException& error)
496 {
497 return error.getResult();
498 }
499
500 return Result::ok();
501}
502
503String JSON::toString (const var& data, const bool allOnOneLine, int maximumDecimalPlaces)
504{
505 MemoryOutputStream mo (1024);
506 JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces);
507 return mo.toUTF8();
508}
509
510void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine, int maximumDecimalPlaces)
511{
512 JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces);
513}
514
516{
519 return mo.toString();
520}
521
522Result JSON::parseQuotedString (String::CharPointerType& t, var& result)
523{
524 try
525 {
526 JSONParser parser (t);
527 auto quote = parser.readChar();
528
529 if (quote != '"' && quote != '\'')
530 return Result::fail ("Not a quoted string!");
531
532 result = parser.parseString (quote);
533 t = parser.currentLocation;
534 }
535 catch (const JSONParser::ErrorException& error)
536 {
537 return error.getResult();
538 }
539
540 return Result::ok();
541}
542
543
544//==============================================================================
545//==============================================================================
546#if JUCE_UNIT_TESTS
547
548class JSONTests : public UnitTest
549{
550public:
551 JSONTests()
552 : UnitTest ("JSON", UnitTestCategories::json)
553 {}
554
555 static String createRandomWideCharString (Random& r)
556 {
557 juce_wchar buffer[40] = { 0 };
558
559 for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
560 {
561 if (r.nextBool())
562 {
563 do
564 {
565 buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
566 }
567 while (! CharPointer_UTF16::canRepresent (buffer[i]));
568 }
569 else
570 buffer[i] = (juce_wchar) (1 + r.nextInt (0xff));
571 }
572
573 return CharPointer_UTF32 (buffer);
574 }
575
576 static String createRandomIdentifier (Random& r)
577 {
578 char buffer[30] = { 0 };
579
580 for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
581 {
582 static const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
583 buffer[i] = chars [r.nextInt (sizeof (chars) - 1)];
584 }
585
586 return CharPointer_ASCII (buffer);
587 }
588
589 // Creates a random double that can be easily stringified, to avoid
590 // false failures when decimal places are rounded or truncated slightly
591 static var createRandomDouble (Random& r)
592 {
593 return var ((r.nextDouble() * 1000.0) + 0.1);
594 }
595
596 static var createRandomVar (Random& r, int depth)
597 {
598 switch (r.nextInt (depth > 3 ? 6 : 8))
599 {
600 case 0: return {};
601 case 1: return r.nextInt();
602 case 2: return r.nextInt64();
603 case 3: return r.nextBool();
604 case 4: return createRandomDouble (r);
605 case 5: return createRandomWideCharString (r);
606
607 case 6:
608 {
609 var v (createRandomVar (r, depth + 1));
610
611 for (int i = 1 + r.nextInt (30); --i >= 0;)
612 v.append (createRandomVar (r, depth + 1));
613
614 return v;
615 }
616
617 case 7:
618 {
619 auto o = new DynamicObject();
620
621 for (int i = r.nextInt (30); --i >= 0;)
622 o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1));
623
624 return o;
625 }
626
627 default:
628 return {};
629 }
630 }
631
632 void runTest() override
633 {
634 {
635 beginTest ("JSON");
636
637 auto r = getRandom();
638
639 expect (JSON::parse (String()) == var());
640 expect (JSON::parse ("{}").isObject());
641 expect (JSON::parse ("[]").isArray());
642 expect (JSON::parse ("[ 1234 ]")[0].isInt());
643 expect (JSON::parse ("[ 12345678901234 ]")[0].isInt64());
644 expect (JSON::parse ("[ 1.123e3 ]")[0].isDouble());
645 expect (JSON::parse ("[ -1234]")[0].isInt());
646 expect (JSON::parse ("[-12345678901234]")[0].isInt64());
647 expect (JSON::parse ("[-1.123e3]")[0].isDouble());
648
649 for (int i = 100; --i >= 0;)
650 {
651 var v;
652
653 if (i > 0)
654 v = createRandomVar (r, 0);
655
656 const bool oneLine = r.nextBool();
657 String asString (JSON::toString (v, oneLine));
658 var parsed = JSON::parse ("[" + asString + "]")[0];
659 String parsedString (JSON::toString (parsed, oneLine));
660 expect (asString.isNotEmpty() && parsedString == asString);
661 }
662 }
663
664 {
665 beginTest ("Float formatting");
666
667 std::map<double, String> tests;
668 tests[1] = "1.0";
669 tests[1.1] = "1.1";
670 tests[1.01] = "1.01";
671 tests[0.76378] = "0.76378";
672 tests[-10] = "-10.0";
673 tests[10.01] = "10.01";
674 tests[0.0123] = "0.0123";
675 tests[-3.7e-27] = "-3.7e-27";
676 tests[1e+40] = "1.0e40";
677 tests[-12345678901234567.0] = "-1.234567890123457e16";
678 tests[192000] = "192000.0";
679 tests[1234567] = "1.234567e6";
680 tests[0.00006] = "0.00006";
681 tests[0.000006] = "6.0e-6";
682
683 for (auto& test : tests)
684 expectEquals (JSON::toString (test.first), test.second);
685 }
686 }
687};
688
689static JSONTests JSONUnitTests;
690
691#endif
692
693} // namespace juce
static void message(int level, const char *fmt,...)
Definition adplugdb.cpp:120
static Result ok() noexcept
Definition Result.h:68
static Result fail(const std::string &errorMessage) noexcept
Definition Result.cpp:58
Definition String.h:48
static String toHexString(int number)
Definition String.cpp:1830
Definition juce_Array.h:56
bool isEmpty() const noexcept
Definition juce_Array.h:222
int size() const noexcept
Definition juce_Array.h:215
ElementType & getReference(int index) noexcept
Definition juce_Array.h:267
Definition juce_CharPointer_UTF16.h:35
static size_t getBytesRequiredFor(juce_wchar charToWrite) noexcept
Definition juce_CharPointer_UTF16.h:248
void write(juce_wchar charToWrite) noexcept
Definition juce_CharPointer_UTF16.h:181
int16 CharType
Definition juce_CharPointer_UTF16.h:40
static bool isWhitespace(char character) noexcept
Definition juce_CharacterFunctions.cpp:59
static double readDoubleValue(CharPointerType &text) noexcept
Definition juce_CharacterFunctions.h:147
static int getHexDigitValue(juce_wchar digit) noexcept
Definition juce_CharacterFunctions.cpp:112
Definition juce_DynamicObject.h:40
Definition juce_File.h:45
Definition juce_Identifier.h:39
bool isValid() const noexcept
Definition juce_Identifier.h:114
Definition juce_InputStream.h:37
virtual String readEntireStreamAsString()
Definition juce_InputStream.cpp:229
static var fromString(StringRef)
Definition juce_JSON.cpp:468
static Result parse(const String &text, var &parsedResult)
Definition juce_JSON.cpp:489
static String escapeString(StringRef)
Definition juce_JSON.cpp:515
static String toString(const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
Definition juce_JSON.cpp:503
static void writeToStream(OutputStream &output, const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
Definition juce_JSON.cpp:510
static Result parseQuotedString(String::CharPointerType &text, var &result)
Definition juce_JSON.cpp:522
Definition juce_MemoryOutputStream.h:36
Definition juce_OutputStream.h:38
Definition juce_Result.h:57
Definition juce_String.h:53
Definition juce_StringRef.h:62
Definition juce_UnitTest.h:70
Definition juce_Variant.h:42
String paddedLeft(water_uchar padCharacter, int minimumLength) const
Definition String.cpp:1042
* e
Definition inflate.c:1404
struct huft * t
Definition inflate.c:943
unsigned v[N_MAX]
Definition inflate.c:1584
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
static PuglViewHint int value
Definition pugl.h:1708
JSAMPIMAGE data
Definition jpeglib.h:945
#define jassert(expression)
#define jassertfalse
static struct TestCase tests[]
Definition lilv_test.c:2218
float out
Definition lilv_test.c:1461
Definition juce_UnitTestCategories.h:27
JOCTET * buffer
Definition juce_JPEGLoader.cpp:302
Definition carla_juce.cpp:31
NewLine newLine
Definition juce_String.cpp:28
bool juce_isfinite(NumericType) noexcept
Definition juce_MathsFunctions.h:421
static String serialiseDouble(double input)
Definition juce_String.cpp:2260
long long int64
Definition juce_MathsFunctions.h:54
wchar_t juce_wchar
Definition juce_CharacterFunctions.h:42
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Definition juce_MathsFunctions.h:279
constexpr int numElementsInArray(Type(&)[N]) noexcept
Definition juce_MathsFunctions.h:344
static int test(SerdEnv *env, bool top_level, bool pretty_numbers)
Definition sratom_test.c:79
Definition juce_JSON.cpp:308
static void writeSpaces(OutputStream &out, int numSpaces)
Definition juce_JSON.cpp:414
static void writeEscapedChar(OutputStream &out, const unsigned short value)
Definition juce_JSON.cpp:363
static void write(OutputStream &out, const var &v, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces)
Definition juce_JSON.cpp:309
@ indentSize
Definition juce_JSON.cpp:454
static void writeArray(OutputStream &out, const Array< var > &array, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces)
Definition juce_JSON.cpp:419
static void writeString(OutputStream &out, String::CharPointerType t)
Definition juce_JSON.cpp:368
Definition juce_JSON.cpp:33
int column
Definition juce_JSON.cpp:35
Result getResult() const
Definition juce_JSON.cpp:38
String getDescription() const
Definition juce_JSON.cpp:37
int line
Definition juce_JSON.cpp:35
String message
Definition juce_JSON.cpp:34
Definition juce_JSON.cpp:27
var parseNumber(bool isNegative)
Definition juce_JSON.cpp:189
bool matchIf(char c)
Definition juce_JSON.cpp:58
bool isEOF() const
Definition juce_JSON.cpp:59
void throwError(juce::String message, String::CharPointerType location)
Definition juce_JSON.cpp:41
String parseString(const juce_wchar quoteChar)
Definition juce_JSON.cpp:83
JSONParser(String::CharPointerType text)
Definition juce_JSON.cpp:28
var parseArray()
Definition juce_JSON.cpp:277
juce_wchar peekChar() const
Definition juce_JSON.cpp:57
var parseObjectOrArray()
Definition juce_JSON.cpp:70
String::CharPointerType startLocation
Definition juce_JSON.cpp:30
bool matchString(const char *t)
Definition juce_JSON.cpp:61
void skipWhitespace()
Definition juce_JSON.cpp:55
juce_wchar readChar()
Definition juce_JSON.cpp:56
var parseObject()
Definition juce_JSON.cpp:231
String::CharPointerType currentLocation
Definition juce_JSON.cpp:30
var parseAny()
Definition juce_JSON.cpp:143
const char * text
Definition swell-functions.h:167
return c
Definition crypt.c:175
int r
Definition crypt.c:458
int error
Definition extract.c:1038
int result
Definition process.c:1455
typedef int(UZ_EXP MsgFn)()
struct zdirent * file
Definition win32.c:1500
mo
Definition zipinfo.c:2287