diff --git a/modules/juce_core/containers/juce_Variant.cpp b/modules/juce_core/containers/juce_Variant.cpp index 0b45553bef..4c3b934436 100644 --- a/modules/juce_core/containers/juce_Variant.cpp +++ b/modules/juce_core/containers/juce_Variant.cpp @@ -708,6 +708,21 @@ bool var::hasProperty (const Identifier& propertyName) const noexcept return false; } +juce::StringArray var::getProperties() const +{ + if (auto* o = getDynamicObject()) + { + juce::StringArray names; + + for (auto itr : o->getProperties()) + names.add (itr.name.toString()); + + return names; + } + + return {}; +} + var::NativeFunction var::getNativeFunction() const { return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr; @@ -893,6 +908,8 @@ var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int { } +//============================================================================== + //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES diff --git a/modules/juce_core/containers/juce_Variant.h b/modules/juce_core/containers/juce_Variant.h index 3e70efd3f6..a6bcb97b8d 100644 --- a/modules/juce_core/containers/juce_Variant.h +++ b/modules/juce_core/containers/juce_Variant.h @@ -262,6 +262,8 @@ public: var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const; /** Returns true if this variant is an object and if it has the given property. */ bool hasProperty (const Identifier& propertyName) const noexcept; + /** Returns property names if this variant is an object. */ + juce::StringArray getProperties() const; /** Invokes a named method call with no arguments. */ var call (const Identifier& method) const; diff --git a/modules/juce_core/json/juce_JSONUtils.cpp b/modules/juce_core/json/juce_JSONUtils.cpp index 3652023af6..3d96967f23 100644 --- a/modules/juce_core/json/juce_JSONUtils.cpp +++ b/modules/juce_core/json/juce_JSONUtils.cpp @@ -124,6 +124,57 @@ std::optional JSONUtils::setPointer (const var& v, return {}; } +var JSONUtils::getPointer (const var& v, String pointer, const var& defaultValue) +{ + if (pointer.isEmpty()) + return defaultValue; + + 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()) + return object->hasProperty (unescaped) ? object->getProperty (unescaped) : defaultValue; + else + return getPointer (object->getProperty (unescaped), tail, defaultValue); + } + 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()) + return isPositiveAndBelow (index, array->size()) ? (*array)[int (index)] : defaultValue; + else + return getPointer ((*array)[(int) index], tail, defaultValue); + } + + return defaultValue; +} + bool JSONUtils::deepEqual (const var& a, const var& b) { const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y) diff --git a/modules/juce_core/json/juce_JSONUtils.h b/modules/juce_core/json/juce_JSONUtils.h index 5ea77bab21..16c0bdb6b5 100644 --- a/modules/juce_core/json/juce_JSONUtils.h +++ b/modules/juce_core/json/juce_JSONUtils.h @@ -57,6 +57,17 @@ 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, + returns the value of the property or array index referenced by the pointer + + If the pointer cannot be followed, due to referencing missing array indices + or fields, then this returns defaultValue. + + For more details, check the JSON Pointer RFC 6901: + https://datatracker.ietf.org/doc/html/rfc6901 + */ + static var getPointer (const var& v, String pointer, const var& defaultValue); + /** Converts the provided key/value pairs into a JSON object. */ static var makeObject (const std::map& source);