// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Algo/AnyOf.h" #include "Dom/JsonObject.h" #include "Dom/JsonValue.h" #include "Misc/TVariant.h" namespace UE::Json { // @note: Concepts are an easier way (than type traits) of checking if a call or specialization is valid namespace Concepts { /** Concept to check if T has NumericLimits, and isn't a bool. */ struct CNumerical { template auto Requires() -> decltype( TAnd< TIsSame::NumericType, T>, TNot>>::Value); }; /** Describes a type that provides a FromJson function. */ struct CFromJsonable { template auto Requires(T& Val, const TSharedRef& Arg) -> decltype( Val.FromJson(Arg) ); }; /** Describes a type the is derived from TMap. */ struct CMap { template auto Requires() -> decltype( TOr< TIsTMap, TIsDerivedFrom>>::Value); }; } namespace TypeTraits { /** String-like value types. */ template struct TIsStringLike { enum { Value = false }; }; template struct TIsStringLike< ValueType, typename TEnableIf< TOr< TIsSame, TIsSame, TIsSame>::Value>::Type> { enum { Value = true }; }; /** Numeric value types. */ template using TIsNumeric = TAnd>>; /** ValueType has FromJson. */ template using THasFromJson = TModels>; /** ValueType is derived from TMap. */ template using TIsDerivedFromMap = TModels>; } /** Contains either an object constructed in place, or a reference to an object declared elsewhere. */ template class TJsonReference { public: using ElementType = ObjectType; static constexpr ESPMode Mode = ESPMode::ThreadSafe; /** Constructs an empty Json Reference. */ TJsonReference() : Object(nullptr) { } /** Sets the object path and flags it as a pending reference (to be resolved later). */ void ResolveDeferred(const FString& InJsonPath) { bHasPendingResolve = true; Path = InJsonPath; } /** Attempts to resolve the reference, returns true if successful or already set. */ bool TryResolve(const TSharedRef& InRootObject, const TFunctionRef&)>& InSetter) { // Already attempted to resolve, return result or nullptr if(!bHasPendingResolve) { return Object.IsValid(); } TArray PathSegments; GetPathSegments(PathSegments); PathSegments.RemoveAt(0); // Remove #/ const TSharedPtr RootObject = InRootObject; const TSharedPtr* SubObject = &RootObject; for(const FString& PathSegment : PathSegments) { if(!(*SubObject)->TryGetObjectField(PathSegment, SubObject)) { return false; } } Object = MoveTemp(InSetter(SubObject->ToSharedRef())); return Object.IsValid(); } /** Returns the object referenced by this Json Reference, creating it if it doesn't exist. */ ObjectType* Get() { if(!Object.IsValid()) { Object = MakeShared(); } return Object.Get(); } /** Returns the object referenced by this Json Reference, creating it if it doesn't exist. */ TSharedPtr GetShared() { if(!Object.IsValid()) { Object = MakeShared(); } return Object; } /** Returns the object referenced by this Json Reference, or nullptr if it doesn't exist. */ ObjectType* Get() const { return Object.Get(); } /** Returns the object referenced by this Json Reference, or nullptr if it doesn't exist. */ TSharedPtr GetShared() const { return Object; } /** Set's the object if it's not already set. */ bool Set(ObjectType&& InObject) { if(Object.IsValid()) { return false; } bHasPendingResolve = false; Object = MoveTemp(InObject); return true; } /** Set's the object if it's not already set. */ bool Set(TSharedPtr&& InObject) { if(Object.IsValid()) { return false; } bHasPendingResolve = false; Object = MoveTemp(InObject); return true; } /** Set's the object if it's not already set. */ bool Set(const TSharedPtr& InObject) { if(Object.IsValid()) { return false; } bHasPendingResolve = false; Object = InObject; return true; } /** Checks if the underlying object has been set. */ bool IsSet() const { return Object.IsValid(); } /** Checks to see if this is actually pointing to an object. */ explicit operator bool() const { return Object != nullptr; } /** Checks if the object is valid, or it's pending reference resolution. */ bool IsValid() const { return Object != nullptr || bHasPendingResolve; } /** Dereferences the object*/ ObjectType& operator*() const { check(IsValid()); return *Object; } /** Pointer to the underlying object. */ ObjectType* operator->() const { check(IsValid()); return Get(); } const FString& GetPath() const { return Path; } /** Returns true if there were one or more segments. */ bool GetPathSegments(TArray& OutSegments) const { return Path.ParseIntoArray(OutSegments, TEXT("/")) > 0; } FString GetLastPathSegment() const { TArray PathSegments; if(GetPathSegments(PathSegments)) { return PathSegments.Last(); } return TEXT(""); } protected: /** The specified path to the actual object definition. */ FString Path; /** Flag indicating this is a reference but not yet resolved. */ bool bHasPendingResolve = false; /** Underling object pointer. */ TSharedPtr Object; }; // Numeric template constexpr typename TEnableIf::Value, void>::Type As(const TSharedPtr& InJsonValue, ValueType& OutValue) { OutValue = InJsonValue->AsNumber(); } // String template constexpr typename TEnableIf::Value, void>::Type As(const TSharedPtr& InJsonValue, ValueType& OutValue) { OutValue = InJsonValue->AsString(); } // Bool template constexpr typename TEnableIf::Value, void>::Type As(const TSharedPtr& InJsonValue, ValueType& OutValue) { OutValue = InJsonValue->AsBool(); } // Numeric template constexpr typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // Enum template typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, EnumType& OutValue, const TMap& InNameToValue = {}); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, EnumType& OutValue, const TMap& InNameToValue = {}); // String template constexpr typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // Bool template constexpr typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // Array template typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ContainerType& OutValues); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ContainerType& OutValues); // Map template typename TEnableIf::Value, bool>::Type // Key must be string TryGet(const TSharedPtr& InJsonValue, TMap& OutValues); template constexpr typename TEnableIf::Value, bool>::Type // Key must be string TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TMap& OutValues); // Key must be string template typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, MapType& OutValues); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, MapType& OutValues); // Key must be string // Object (with FromJson) template typename TEnableIf< TAnd< TNot>, TypeTraits::THasFromJson>::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template typename TEnableIf< TAnd< TNot>, TypeTraits::THasFromJson>::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // Variant template bool TryGet(const TSharedPtr& InJsonValue, TVariant& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TVariant& OutValue); // UniqueObj template bool TryGet(const TSharedPtr& InJsonValue, TUniqueObj& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TUniqueObj& OutValue); // TJsonReference template bool TryGet(const TSharedPtr& InJsonValue, TJsonReference& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TJsonReference& OutValue); template typename TEnableIf>::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template typename TEnableIf>::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // Object (with FromJson) template typename TEnableIf< TAnd< TNot>, TypeTraits::THasFromJson>::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template typename TEnableIf< TAnd< TNot>, TypeTraits::THasFromJson>::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // Object (without FromJson) template typename TEnableIf< TAnd< TNot>>, TNot>>, TNot>, TNot>, TNot>, TNot>, TNot>>::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); // TIsTMap, template typename TEnableIf< TAnd< TNot>>, TNot>>, TNot>, TNot>, TNot>, TNot>, TNot>>::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // UniqueObj template bool TryGet(const TSharedPtr& InJsonValue, TUniqueObj& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TUniqueObj& OutValue); // SharedPtr template bool TryGet(const TSharedPtr& InJsonValue, TSharedPtr& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TSharedPtr& OutValue); template typename TEnableIf>::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template typename TEnableIf>::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, ValueType& OutValue); // SharedRef template bool TryGet(const TSharedPtr& InJsonValue, TSharedRef& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TSharedRef& OutValue); // Optional template constexpr typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, TOptional& OutValue); template typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TOptional& OutValue); // Optional Enum template constexpr typename TEnableIf::Value, bool>::Type TryGet(const TSharedPtr& InJsonValue, TOptional& OutValue, const TMap& InNameToValue = {}); template constexpr typename TEnableIf::Value, bool>::Type TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TOptional& OutValue, const TMap& InNameToValue = {}); // Optional UniqueObj template bool TryGet(const TSharedPtr& InJsonValue, TOptional>& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TOptional>& OutValue); // Optional SharedRef template bool TryGet(const TSharedPtr& InJsonValue, TOptional>& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TOptional>& OutValue); // JsonObject template >::Value>::Type> bool TryGet(const TSharedPtr& InJsonValue, ValueType& OutValue); template >::Value>::Type> bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TSharedPtr& OutValue); // Variant struct FVariantValueVisitor { explicit FVariantValueVisitor(const TSharedPtr& InJsonValue) : JsonValue(InJsonValue) { } template bool operator()(ValueType& OutValue) { ValueType TempValue; if(UE::Json::TryGet(JsonValue, TempValue)) { OutValue = MoveTemp(TempValue); return true; } return false; } const TSharedPtr JsonValue; }; struct FVariantObjectVisitor { explicit FVariantObjectVisitor(const TSharedPtr& InJsonObject, const FString& InFieldName) : JsonObject(InJsonObject) , FieldName(InFieldName) { } template bool operator()(ValueType& OutValue) { if(UE::Json::TryGetField(JsonObject, FieldName, OutValue)) { return true; } return false; } const TSharedPtr JsonObject; const FString FieldName; }; template bool TryGet(const TSharedPtr& InJsonValue, TVariant& OutValue); template bool TryGetField(const TSharedPtr& InJsonObject, const FString& InFieldName, TVariant& OutValue); } #include "WebAPIJsonUtilities.inl"