Files
UnrealEngine/Engine/Source/Runtime/Json/Public/JsonUtils/JsonObjectArrayUpdater.h
2025-05-18 13:04:45 +08:00

98 lines
3.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Dom/JsonObject.h"
#include "Dom/JsonValue.h"
/**
* Utility to update an array of json objects from an array of elements (of arbitrary type).
* Elements in the source array and the destination json object array are matched based on an
* arbitrary key (provided by the FGetElementKey and FTryGetJsonObjectKey delegates respectively).
* Existing elements get "updated" via the FUpdateJsonObject delegate. The update scheme is entirely
* customizable; for example, it can be non-destructive and leave some json fields unchanged.
* Elements from the source array that are not in the json array (based on the "key") are added to it.
* Elements that are not present in the source array (based on the "key") are removed from the json array.
* If the source array is empty the json array field is removed.
*/
template <typename ElementType, typename KeyType>
struct FJsonObjectArrayUpdater
{
DECLARE_DELEGATE_RetVal_OneParam(KeyType, FGetElementKey, const ElementType&);
DECLARE_DELEGATE_RetVal_TwoParams(bool, FTryGetJsonObjectKey, const FJsonObject&, KeyType& /*OutKey*/);
DECLARE_DELEGATE_TwoParams(FUpdateJsonObject, const ElementType&, FJsonObject&);
DECLARE_DELEGATE_OneParam(FSortArray, TArray<TSharedPtr<FJsonValue>>&);
static void Execute(FJsonObject& JsonObject, const FString& ArrayName, const TArray<ElementType>& SourceArray, FGetElementKey GetElementKey, FTryGetJsonObjectKey TryGetJsonObjectKey, FUpdateJsonObject UpdateJsonObject, FSortArray SortJsonArray = FSortArray())
{
if (SourceArray.Num() > 0)
{
TArray<TSharedPtr<FJsonValue>> NewJsonValues;
{
const TArray<TSharedPtr<FJsonValue>>* ExistingJsonValues;
if (JsonObject.TryGetArrayField(ArrayName, ExistingJsonValues))
{
// Build a map of elements for quick access and to keep track of which ones got updated
TMap<KeyType, const ElementType*> ElementsMap;
for (const ElementType& Element : SourceArray)
{
ElementsMap.Add(GetElementKey.Execute(Element), &Element);
}
// Update existing json values and discard entries that no longer exist or are invalid
for (TSharedPtr<FJsonValue> ExistingJsonValue : *ExistingJsonValues)
{
const TSharedPtr<FJsonObject>* ExistingJsonValueAsObject;
if (ExistingJsonValue->TryGetObject(ExistingJsonValueAsObject))
{
KeyType ElementKey;
if (TryGetJsonObjectKey.Execute(**ExistingJsonValueAsObject, ElementKey))
{
if (const ElementType** ElementPtr = ElementsMap.Find(ElementKey))
{
UpdateJsonObject.Execute(**ElementPtr, **ExistingJsonValueAsObject);
NewJsonValues.Add(ExistingJsonValue);
ElementsMap.Remove(ElementKey);
}
}
}
}
// Add new elements
for (auto It = ElementsMap.CreateConstIterator(); It; ++It)
{
TSharedPtr<FJsonObject> NewJsonObject = MakeShareable(new FJsonObject);
UpdateJsonObject.Execute(*It.Value(), *NewJsonObject.Get());
NewJsonValues.Add(MakeShareable(new FJsonValueObject(NewJsonObject)));
}
}
else
{
// Array doesn't exist in the given JsonObject, so build a new array
for (const ElementType& Element : SourceArray)
{
TSharedPtr<FJsonObject> NewJsonObject = MakeShareable(new FJsonObject);
UpdateJsonObject.Execute(Element, *NewJsonObject.Get());
NewJsonValues.Add(MakeShareable(new FJsonValueObject(NewJsonObject)));
}
}
}
// Optionally sort new json array before setting incase the user doesn't want the default
// appending of new items at the end.
SortJsonArray.ExecuteIfBound(NewJsonValues);
// Set the new content of the json array
JsonObject.SetArrayField(ArrayName, NewJsonValues);
}
else
{
// Source array is empty so remove the json array
JsonObject.RemoveField(ArrayName);
}
}
};