28 auto result = std::make_unique<DynamicObject>();
30 for (
const auto& [name, value] : source)
31 result->setProperty (name, value);
33 return var (result.release());
39 auto result = std::make_unique<DynamicObject>();
41 if (
const auto iter = source.find (key); iter != source.end())
42 result->setProperty (key, iter->second);
44 for (
const auto& [name, value] : source)
46 result->setProperty (name, value);
48 return var (result.release());
65 const auto findResult = pointer.
indexOfChar (1,
'/');
66 const auto pos = findResult < 0 ? pointer.
length() : findResult;
70 const auto unescaped = head.
replace (
"~1",
"/").
replace (
"~0",
"~");
72 if (
auto*
object = v.getDynamicObject())
74 if (
const auto newProperty =
setPointer (object->getProperty (unescaped), tail, newValue))
76 auto cloned =
object->clone();
77 cloned->setProperty (unescaped, *newProperty);
78 return var (cloned.release());
83 const auto index = [&]() ->
size_t
86 return (
size_t) array->size();
91 if (! unescaped.startsWith (
"0"))
92 return (
size_t) unescaped.getLargeIntValue();
94 return std::numeric_limits<size_t>::max();
97 if (
const auto newIndex =
setPointer ((*array)[(
int) index], tail, newValue))
101 if ((
int) index == copied.size())
104 if (isPositiveAndBelow (index, copied.size()))
106 copied.getReference ((
int) index) = *newIndex;
115bool JSONUtils::deepEqual (
const var& a,
const var& b)
124 if (! y.hasProperty (key))
127 if (! deepEqual (value, y.getProperty (key)))
134 if (
auto* i = a.getDynamicObject())
135 if (
auto* j = b.getDynamicObject())
136 return compareObjects (*i, *j);
140 return std::equal (i->begin(), i->end(), j->begin(), j->end(), [] (
const var& x,
const var& y) {
return deepEqual (x, y); });
149class JSONUtilsTests final :
public UnitTest
152 JSONUtilsTests() :
UnitTest (
"JSONUtils", UnitTestCategories::json) {}
158 const auto obj =
JSON::parse (R
"({ "name": "PIANO 4"
160 , "lfoWaveform": "triangle"
161 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
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"
172 , "lfoWaveform": "triangle"
173 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
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"
181 , "lfoWaveform": "triangle"
182 , "pitchEnvelope": { "rates": [80,67,95,60], "levels": [50,50,50,50] }
184 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/0", 80), JSON::parse (R
"({ "name": "PIANO 4"
186 , "lfoWaveform": "triangle"
187 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [80,50,50,50] }
189 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/-", 100), JSON::parse (R
"({ "name": "PIANO 4"
191 , "lfoWaveform": "triangle"
192 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50,100] }
197 void expectDeepEqual (
const std::optional<var>& a,
const std::optional<var>& b)
199 const auto text = a.has_value() && b.has_value()
202 expect (deepEqual (a, b), text);
205 static bool deepEqual (
const std::optional<var>& a,
const std::optional<var>& b)
207 if (a.has_value() && b.has_value())
214static JSONUtilsTests jsonUtilsTests;
NamedValueSet & getProperties() noexcept
static Result parse(const String &text, var &parsedResult)
static String toString(const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
int size() const noexcept
CharPointerType begin() const
int indexOfChar(juce_wchar characterToLookFor) const noexcept
int length() const noexcept
bool isEmpty() const noexcept
bool startsWith(StringRef text) const noexcept
CharPointerType end() const
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
Array< var > * getArray() const noexcept
static std::optional< var > setPointer(const var &v, String pointer, const var &newValue)
static bool deepEqual(const var &a, const var &b)
static var makeObject(const std::map< Identifier, var > &source)
static var makeObjectWithKeyFirst(const std::map< Identifier, var > &source, Identifier key)