From f7d86f118a849f06715ec1a8e35a63554a5a0579 Mon Sep 17 00:00:00 2001 From: Roland Rabien Date: Tue, 19 Nov 2024 09:10:27 -0800 Subject: [PATCH] Added JSONUtils::updatePointer --- modules/juce_core/json/juce_JSONUtils.cpp | 68 +++++++++++++++++++++++ modules/juce_core/json/juce_JSONUtils.h | 12 ++++ 2 files changed, 80 insertions(+) diff --git a/modules/juce_core/json/juce_JSONUtils.cpp b/modules/juce_core/json/juce_JSONUtils.cpp index a17bf947ce9..3f0b56a405d 100644 --- a/modules/juce_core/json/juce_JSONUtils.cpp +++ b/modules/juce_core/json/juce_JSONUtils.cpp @@ -124,6 +124,74 @@ std::optional JSONUtils::setPointer (const var& v, return {}; } +bool JSONUtils::updatePointer (var& v, String pointer, const var& newValue) +{ + if (pointer.isEmpty()) + return false; + + if (! pointer.startsWith ("/")) + { + // This is not a well-formed JSON pointer + jassertfalse; + return {}; + } + + const auto findResult = pointer.indexOfChar (1, '/'); + const auto pos = findResult < 0 ? pointer.length() : findResult; + const String head (pointer.begin() + 1, pointer.begin() + pos); + const String tail (pointer.begin() + pos, pointer.end()); + + const auto unescaped = head.replace ("~1", "/").replace ("~0", "~"); + + if (auto* object = v.getDynamicObject()) + { + if (tail.isEmpty()) + { + object->setProperty (unescaped, newValue); + return true; + } + + auto v = object->getProperty (unescaped); + return updatePointer (v, tail, newValue); + } + else if (auto* array = v.getArray()) + { + const auto index = [&]() -> size_t + { + if (unescaped == "-") + return (size_t) array->size(); + + if (unescaped == "0") + return 0; + + if (! unescaped.startsWith ("0")) + return (size_t) unescaped.getLargeIntValue(); + + return std::numeric_limits::max(); + }(); + + if (tail.isEmpty()) + { + if (isPositiveAndBelow (index, array->size())) + { + array->set (int (index), newValue); + return true; + } + + if (index == array->size()) + { + array->add (newValue); + return true; + } + } + + auto v = (*array)[(int) index]; + return updatePointer (v, tail, newValue); + } + + return false; +} + var JSONUtils::getPointer (const var& v, String pointer, const var& defaultValue) { if (pointer.isEmpty()) diff --git a/modules/juce_core/json/juce_JSONUtils.h b/modules/juce_core/json/juce_JSONUtils.h index 16c0bdb6b57..dc6d69dc9a5 100644 --- a/modules/juce_core/json/juce_JSONUtils.h +++ b/modules/juce_core/json/juce_JSONUtils.h @@ -57,6 +57,18 @@ struct JSONUtils */ static std::optional 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 'newValue', updates 'v' where the + property or array index referenced by the pointer has been set to 'newValue'. + + If the pointer cannot be followed, due to referencing missing array indices + or fields, then this returns false. + + For more details, check the JSON Pointer RFC 6901: + https://datatracker.ietf.org/doc/html/rfc6901 + */ + static bool updatePointer (var& v, String pointer, const var& newValue); + /** Given a JSON array/object 'v', a string representing a JSON pointer, returns the value of the property or array index referenced by the pointer