JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_JSONUtils.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 auto result = std::make_unique<DynamicObject>();
29
30 for (const auto& [name, value] : source)
31 result->setProperty (name, value);
32
33 return var (result.release());
34}
35
37 Identifier key)
38{
39 auto result = std::make_unique<DynamicObject>();
40
41 if (const auto iter = source.find (key); iter != source.end())
42 result->setProperty (key, iter->second);
43
44 for (const auto& [name, value] : source)
45 if (name != key)
46 result->setProperty (name, value);
47
48 return var (result.release());
49}
50
52 String pointer,
53 const var& newValue)
54{
55 if (pointer.isEmpty())
56 return newValue;
57
58 if (! pointer.startsWith ("/"))
59 {
60 // This is not a well-formed JSON pointer
62 return {};
63 }
64
65 const auto findResult = pointer.indexOfChar (1, '/');
66 const auto pos = findResult < 0 ? pointer.length() : findResult;
67 const String head (pointer.begin() + 1, pointer.begin() + pos);
68 const String tail (pointer.begin() + pos, pointer.end());
69
70 const auto unescaped = head.replace ("~1", "/").replace ("~0", "~");
71
72 if (auto* object = v.getDynamicObject())
73 {
74 if (const auto newProperty = setPointer (object->getProperty (unescaped), tail, newValue))
75 {
76 auto cloned = object->clone();
77 cloned->setProperty (unescaped, *newProperty);
78 return var (cloned.release());
79 }
80 }
81 else if (auto* array = v.getArray())
82 {
83 const auto index = [&]() -> size_t
84 {
85 if (unescaped == "-")
86 return (size_t) array->size();
87
88 if (unescaped == "0")
89 return 0;
90
91 if (! unescaped.startsWith ("0"))
92 return (size_t) unescaped.getLargeIntValue();
93
95 }();
96
97 if (const auto newIndex = setPointer ((*array)[(int) index], tail, newValue))
98 {
99 auto copied = *array;
100
101 if ((int) index == copied.size())
102 copied.add ({});
103
104 if (isPositiveAndBelow (index, copied.size()))
105 {
106 copied.getReference ((int) index) = *newIndex;
107 return var (copied);
108 }
109 }
110 }
111
112 return {};
113}
114
115bool JSONUtils::deepEqual (const var& a, const var& b)
116{
117 const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y)
118 {
119 if (x.getProperties().size() != y.getProperties().size())
120 return false;
121
122 for (const auto& [key, value] : x.getProperties())
123 {
124 if (! y.hasProperty (key))
125 return false;
126
127 if (! deepEqual (value, y.getProperty (key)))
128 return false;
129 }
130
131 return true;
132 };
133
134 if (auto* i = a.getDynamicObject())
135 if (auto* j = b.getDynamicObject())
136 return compareObjects (*i, *j);
137
138 if (auto* i = a.getArray())
139 if (auto* j = b.getArray())
140 return std::equal (i->begin(), i->end(), j->begin(), j->end(), [] (const var& x, const var& y) { return deepEqual (x, y); });
141
142 return a == b;
143}
144
145//==============================================================================
146//==============================================================================
147#if JUCE_UNIT_TESTS
148
149class JSONUtilsTests final : public UnitTest
150{
151public:
152 JSONUtilsTests() : UnitTest ("JSONUtils", UnitTestCategories::json) {}
153
154 void runTest() override
155 {
156 beginTest ("JSON pointers");
157 {
158 const auto obj = JSON::parse (R"({ "name": "PIANO 4"
159 , "lfoSpeed": 30
160 , "lfoWaveform": "triangle"
161 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
162 })");
163 expectDeepEqual (JSONUtils::setPointer (obj, "", "hello world"), var ("hello world"));
164 expectDeepEqual (JSONUtils::setPointer (obj, "/lfoWaveform/foobar", "str"), std::nullopt);
165 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/foo", 2), JSON::parse (R"({"foo":2,"bar":1})"));
166 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/baz", 2), JSON::parse (R"({"foo":0,"bar":1,"baz":2})"));
167 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":{},"bar":{}})"), "/foo/bar", 2), JSON::parse (R"({"foo":{"bar":2},"bar":{}})"));
168 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/01", "str"), std::nullopt);
169 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/10", "str"), std::nullopt);
170 expectDeepEqual (JSONUtils::setPointer (obj, "/lfoSpeed", 10), JSON::parse (R"({ "name": "PIANO 4"
171 , "lfoSpeed": 10
172 , "lfoWaveform": "triangle"
173 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
174 })"));
175 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])"));
176 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])"));
177 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"/":"fizz"})"), "/~1", "buzz"), JSON::parse (R"({"/":"buzz"})"));
178 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"~":"fizz"})"), "/~0", "buzz"), JSON::parse (R"({"~":"buzz"})"));
179 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/0", 80), JSON::parse (R"({ "name": "PIANO 4"
180 , "lfoSpeed": 30
181 , "lfoWaveform": "triangle"
182 , "pitchEnvelope": { "rates": [80,67,95,60], "levels": [50,50,50,50] }
183 })"));
184 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/0", 80), JSON::parse (R"({ "name": "PIANO 4"
185 , "lfoSpeed": 30
186 , "lfoWaveform": "triangle"
187 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [80,50,50,50] }
188 })"));
189 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/-", 100), JSON::parse (R"({ "name": "PIANO 4"
190 , "lfoSpeed": 30
191 , "lfoWaveform": "triangle"
192 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50,100] }
193 })"));
194 }
195 }
196
198 {
199 const auto text = a.has_value() && b.has_value()
200 ? JSON::toString (*a) + " != " + JSON::toString (*b)
201 : String();
202 expect (deepEqual (a, b), text);
203 }
204
205 static bool deepEqual (const std::optional<var>& a, const std::optional<var>& b)
206 {
207 if (a.has_value() && b.has_value())
208 return JSONUtils::deepEqual (*a, *b);
209
210 return a == b;
211 }
212};
213
214static JSONUtilsTests jsonUtilsTests;
215
216#endif
217
218} // namespace juce
Represents a dynamically implemented object.
NamedValueSet & getProperties() noexcept
Returns the NamedValueSet that holds the object's properties.
Represents a string identifier, designed for accessing properties by name.
static Result parse(const String &text, var &parsedResult)
Parses a string of JSON-formatted text, and returns a result code containing any parse errors.
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.
int size() const noexcept
Returns the total number of values that the set contains.
The JUCE String class!
Definition juce_String.h:53
CharPointerType begin() const
Returns an iterator pointing at the beginning of the string.
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Searches for a character inside this string.
int length() const noexcept
Returns the number of characters in the string.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
bool startsWith(StringRef text) const noexcept
Tests whether the string begins with another string.
CharPointerType end() const
Returns an iterator pointing at the terminating null of the string.
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
Replaces all occurrences of a substring with another string.
This is a base class for classes that perform a unit test.
void beginTest(const String &testName)
Tells the system that a new subsection of tests is beginning.
void expect(bool testResult, const String &failureMessage=String())
Checks that the result of a test is true, and logs this result.
virtual void runTest()=0
Implement this method in your subclass to actually run your tests.
A variant class, that can be used to hold a range of primitive values.
Array< var > * getArray() const noexcept
If this variant holds an array, this provides access to it.
T end(T... args)
T equal(T... args)
T find(T... args)
#define jassertfalse
This will always cause an assertion failure.
T max(T... args)
JUCE Namespace.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
T has_value(T... args)
static std::optional< var > setPointer(const var &v, String pointer, const var &newValue)
Given a JSON array/object 'v', a string representing a JSON pointer, and a new property value 'newVal...
static bool deepEqual(const var &a, const var &b)
Returns true if and only if the contents of a match the contents of b.
static var makeObject(const std::map< Identifier, var > &source)
Converts the provided key/value pairs into a JSON object.
static var makeObjectWithKeyFirst(const std::map< Identifier, var > &source, Identifier key)
Converts the provided key/value pairs into a JSON object with the provided key at the first position ...