// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "JsonUtils/RapidJsonUtils.h" #include "JsonUtils/JsonConversion.h" #include "Internationalization/Text.h" struct FLocalizationTargetDescriptor; struct FModuleDescriptor; struct FPluginDescriptor; struct FCustomBuildSteps; struct FPluginReferenceDescriptor; struct FPluginDisallowedDescriptor; struct FPluginManifest; namespace UE { namespace Projects { namespace Private { /** * These are utility functions to be as close to the default Json parser as possible, only really necessary * because we have public APIs that take FJsonObjects directly */ bool TryGetBoolField(Json::FConstObject Object, const TCHAR* FieldName, bool& Out); bool TryGetNumberField(Json::FConstObject Object, const TCHAR* FieldName, int32& Out); bool TryGetNumberField(Json::FConstObject Object, const TCHAR* FieldName, uint32& Out); bool TryGetStringField(Json::FConstObject Object, const TCHAR* FieldName, FString& Out); bool TryGetStringField(Json::FConstObject Object, const TCHAR* FieldName, FStringView& Out); bool TryGetStringArrayField(Json::FConstObject Object, const TCHAR* FieldName, TArray& Out); bool TryGetStringArrayField(Json::FConstObject Object, const TCHAR* FieldName, TArray& Out); bool TryGetStringArrayFieldWithDeprecatedFallback(Json::FConstObject Object, const TCHAR* FieldName, const TCHAR* DeprecatedFieldName, TArray& OutArray); FText GetArrayObjectTypeError(const TCHAR* FieldName, int32 Index); FText GetArrayObjectChildParseError(const TCHAR* FieldName, int32 Index, const FText& PropagateError); /** Get the field named FieldName as an array of strings. Returns false if it doesn't exist or any member cannot be converted. */ template inline bool TryGetEnumArrayField(Json::FConstObject Object, const TCHAR* FieldName, TArray& OutArray) { TOptional Array = Json::GetArrayField(Object, FieldName); if (!Array.IsSet()) { return false; } OutArray.Reset(Array->Size()); for (const Json::FValue& JsonValue : *Array) { if (!JsonValue.IsString()) { continue; } TEnum Value; if (LexTryParseString(Value, JsonValue.GetString())) { OutArray.Add(Value); } } return true; } template inline bool TryGetEnumArrayFieldWithDeprecatedFallback(Json::FConstObject Object, const TCHAR* FieldName, const TCHAR* DeprecatedFieldName, TArray& OutArray) { if (TryGetEnumArrayField(Object, FieldName, /*out*/ OutArray)) { return true; } else if (TryGetEnumArrayField(Object, DeprecatedFieldName, /*out*/ OutArray)) { //@TODO: Warn about deprecated field fallback? return true; } else { return false; } } // A private interface as to not expose RapidJson types TOptional Read(Json::FConstObject Object, FPluginManifest& Out); TOptional Read(Json::FConstObject Object, FPluginDescriptor& Out); TOptional Read(Json::FConstObject Object, FModuleDescriptor& Out); TOptional Read(Json::FConstObject Object, FLocalizationTargetDescriptor& Out); TOptional Read(Json::FConstObject Object, FPluginReferenceDescriptor& Out); TOptional Read(Json::FConstObject Object, FPluginDisallowedDescriptor& Out); FCustomBuildSteps ReadCustomBuildSteps(Json::FConstObject Object, const TCHAR* FieldName); // Helper functions to convert and call the rapidjson functions from default json sources template bool ReadFromDefaultJsonHelper(const FJsonObject& Object, FUNCTOR && Functor) { TOptional Document = Json::ConvertSharedJsonToRapidJsonDocument(Object); if (!Document.IsSet()) { return false; } TOptional RootObject = Json::GetRootObject(*Document); if (!RootObject.IsSet()) { return false; } return Functor(*RootObject); } template bool ReadFromDefaultJson(const FJsonObject& Object, TYPE& Out, FText* OutFailReason) { return ReadFromDefaultJsonHelper( Object, [&Out, OutFailReason](Json::FConstObject RootObject) { TOptional ResultError = Read(RootObject, Out); if (ResultError) { if (OutFailReason) { *OutFailReason = *ResultError; } return false; } return true; } ); } template TOptional ReadArray(Json::FConstObject Object, const TCHAR* FieldName, TArray& OutArray) { TOptional ArrayValue = Json::GetArrayField(Object, FieldName); if(ArrayValue.IsSet()) { OutArray.Reserve(ArrayValue->Size()); for (const Json::FValue& ArrayItem : *ArrayValue) { if (ArrayItem.IsObject()) { TOptional EntryResult = Read(ArrayItem.GetObject(), OutArray.Emplace_GetRef()); if (EntryResult.IsSet()) { OutArray.Pop(); return GetArrayObjectChildParseError(FieldName, OutArray.Num(), *EntryResult); } } else { return GetArrayObjectTypeError(FieldName, OutArray.Num()); } } } return {}; } template bool ReadArrayFromDefaultJson(const FJsonObject& Object, const TCHAR* Name, TArray& OutArray, FText* OutFailReason) { return ReadFromDefaultJsonHelper( Object, [Name, &OutArray, OutFailReason](Json::FConstObject RootObject) { TOptional ArrayError = ReadArray(RootObject, Name, OutArray); if (ArrayError) { if (OutFailReason) { *OutFailReason = MoveTemp(*ArrayError); } return false; } return true; } ); } } } }