// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Dom/JsonValue.h" #include "Dom/JsonObject.h" #include "Serialization/JsonReader.h" #include "Serialization/JsonTypes.h" #include "Serialization/JsonWriter.h" class Error; struct FJsonSerializerPolicy_JsonObject { using FValue = TSharedPtr; using FArrayOfValues = TArray>; using FMapOfValues = TSharedPtr; struct StackState { EJson Type; FString Identifier; TArray> Array; TSharedPtr Object; }; struct FElement { FElement( const FValue& InValue ) : Identifier() , Value(InValue) { } FElement( const FMapOfValues& Object ) : Identifier() , Value(MakeShared(Object)) { } FElement( const FArrayOfValues& Array ) : Identifier() , Value(MakeShared(Array)) { } FElement( const FString& InIdentifier, const FValue& InValue ) : Identifier( InIdentifier ) , Value( InValue ) , bIsKeyValuePair( true ) { } FString Identifier; FValue Value; bool bHasBeenProcessed = false; bool bIsKeyValuePair = false; }; JSON_API static bool GetValueFromState(const StackState& State, FValue& OutValue); JSON_API static bool GetValueFromState(const StackState& State, FArrayOfValues& OutArray); JSON_API static bool GetValueFromState(const StackState& State, FMapOfValues& OutMap); JSON_API static void ResetValue(FValue& OutValue); JSON_API static void ReadObjectStart(StackState& State); JSON_API static void ReadObjectEnd(StackState& State, FValue& OutValue); JSON_API static void ReadArrayStart(StackState& State); JSON_API static void ReadArrayEnd(StackState& State, FValue& OutValue); template static void ReadBoolean(TJsonReader& Reader, FValue& OutValue) { OutValue = MakeShared(Reader.GetValueAsBoolean()); } template static void ReadString(TJsonReader& Reader, FValue& OutValue) { using StoredCharType = typename TJsonReader::StoredCharType; OutValue = MakeShared>(Reader.StealInternalValueAsString()); } template static void ReadNumberAsString(TJsonReader& Reader, FValue& OutValue) { using StoredCharType = typename TJsonReader::StoredCharType; OutValue = MakeShared>(Reader.GetValueAsNumberString()); } template static void ReadNumber(TJsonReader& Reader, FValue& OutValue) { OutValue = MakeShared(Reader.GetValueAsNumber()); } JSON_API static void ReadNull(FValue& OutValue); JSON_API static void AddValueToObject(StackState& State, const FString& Identifier, FValue& NewValue); JSON_API static void AddValueToArray(StackState& State, FValue& NewValue); template static bool SerializeIfBool(TSharedRef& Element, TJsonWriter& Writer, bool bWriteValueOnly) { if (Element->Value->Type == EJson::Boolean) { if (bWriteValueOnly) { Writer.WriteValue(Element->Value->AsBool()); } else { Writer.WriteValue(Element->Identifier, Element->Value->AsBool()); } return true; } return false; } template static bool SerializeIfNumber(TSharedRef& Element, TJsonWriter& Writer, bool bWriteValueOnly) { if (Element->Value->Type == EJson::Number) { if (bWriteValueOnly) { if (Element->Value->PreferStringRepresentation()) { Writer.WriteRawJSONValue(Element->Value->AsString()); } else { Writer.WriteValue(Element->Value->AsNumber()); } } else { if (Element->Value->PreferStringRepresentation()) { Writer.WriteRawJSONValue(Element->Identifier, Element->Value->AsString()); } else { Writer.WriteValue(Element->Identifier, Element->Value->AsNumber()); } } return true; } return false; } template static bool SerializeIfString(TSharedRef& Element, TJsonWriter& Writer, bool bWriteValueOnly) { if(Element->Value->Type == EJson::String) { if (bWriteValueOnly) { Writer.WriteValue(Element->Value->AsString()); } else { Writer.WriteValue(Element->Identifier, Element->Value->AsString()); } return true; } return false; } template static bool SerializeIfNull(TSharedRef& Element, TJsonWriter& Writer, bool bWriteValueOnly) { if(Element->Value->Type == EJson::Null) { if (bWriteValueOnly) { Writer.WriteNull(); } else { Writer.WriteNull(Element->Identifier); } return true; } return false; } template static bool SerializeIfArray(TArray>& ElementStack, TSharedRef& Element, TJsonWriter& Writer, bool bWriteValueOnly) { if(Element->Value->Type == EJson::Array) { if (Element->bHasBeenProcessed) { Writer.WriteArrayEnd(); } else { Element->bHasBeenProcessed = true; ElementStack.Push(Element); if (bWriteValueOnly) { Writer.WriteArrayStart(); } else { Writer.WriteArrayStart(Element->Identifier); } FArrayOfValues Values = Element->Value->AsArray(); for (int Index = Values.Num() - 1; Index >= 0; --Index) { ElementStack.Push(MakeShared(Values[Index])); } } return true; } return false; } template static bool SerializeIfObject(TArray>& ElementStack, TSharedRef& Element, TJsonWriter& Writer, bool bWriteValueOnly) { if(Element->Value->Type == EJson::Object) { if (Element->bHasBeenProcessed) { Writer.WriteObjectEnd(); } else { Element->bHasBeenProcessed = true; ElementStack.Push(Element); if (bWriteValueOnly) { Writer.WriteObjectStart(); } else { Writer.WriteObjectStart(Element->Identifier); } TArray Keys; FArrayOfValues Values; FMapOfValues ElementObject = Element->Value->AsObject(); ElementObject->Values.GenerateKeyArray(Keys); ElementObject->Values.GenerateValueArray(Values); check(Keys.Num() == Values.Num()); for (int Index = Values.Num() - 1; Index >= 0; --Index) { ElementStack.Push(MakeShared(Keys[Index], Values[Index])); } } return true; } return false; } }; template class TJsonSerializer { public: enum class EFlags { None = 0, StoreNumbersAsStrings = 1, }; template static bool Deserialize(const TSharedRef>& Reader, typename Policy::FValue& OutValue, EFlags InOptions = EFlags::None) { return Deserialize(*Reader, OutValue, InOptions); } template static bool Deserialize(TJsonReader& Reader, typename Policy::FValue& OutValue, EFlags InOptions = EFlags::None) { typename Policy::StackState State; if (!Deserialize(Reader, /*OUT*/State, InOptions)) { return false; } return Policy::GetValueFromState(State, OutValue); } template static bool Deserialize(const TSharedRef>& Reader, typename Policy::FArrayOfValues& OutArray, EFlags InOptions = EFlags::None) { return Deserialize(*Reader, OutArray, InOptions); } template static bool Deserialize(TJsonReader& Reader, typename Policy::FArrayOfValues& OutArray, EFlags InOptions = EFlags::None) { typename Policy::StackState State; if (!Deserialize(Reader, /*OUT*/State, InOptions)) { return false; } return Policy::GetValueFromState(State, OutArray); } template static bool Deserialize(const TSharedRef>& Reader, typename Policy::FMapOfValues& OutMap, EFlags InOptions = EFlags::None) { return Deserialize(*Reader, OutMap, InOptions); } template static bool Deserialize(TJsonReader& Reader, typename Policy::FMapOfValues& OutMap, EFlags InOptions = EFlags::None) { typename Policy::StackState State; if (!Deserialize(Reader, /*OUT*/State, InOptions)) { return false; } return Policy::GetValueFromState(State, OutMap); } /** * Serialize the passed value and identifier into the writer. * Empty string identifiers will be ignored when the writer is not writing inside of a map of values and only the value will be serialized. * If the writer is in a state where it's currently writing inside of a map of values, then the identifier will always be serialized. * * Json Examples: * - Writer state: { "foo": "bar" * Parameters: Identifier: "" * Value: "baz" * Serialization result: { "foo": "bar", "": "baz" //empty identifier is serialized as a valid key for the key:value pair "":"baz" * * - Writer state: { "foo": ["bar" * Parameters: Identifier: "" * Value: "baz" * Serialization result: { foo: ["bar", "baz" //empty identifier is ignored since we are writing into an array and not an object. * * @param Value The value we are serializing * @param Identifier The identifier of the value, empty identifiers are ignored outside of maps of values. * @param Writer The writer the value and identifier are written into. * @param bCloseWriter When set to true the Writer will be closed after the serialization. * @return Returns true if the serialization was successful, false otherwise. */ template static bool Serialize(const typename Policy::FValue& Value, const FString& Identifier, const TSharedRef>& Writer, bool bCloseWriter = true) { return Serialize(Value, Identifier, *Writer, bCloseWriter); } /** * Serialize the passed value and identifier into the writer. * Empty string identifiers will be ignored when the writer is not writing inside of a map of values and only the value will be serialized. * If the writer is in a state where it's currently writing inside of a map of values, then the identifier will always be serialized. * * Json Examples: * - Writer state: { "foo": "bar" * Parameters: Identifier: "" * Value: "baz" * Serialization result: { "foo": "bar", "": "baz" //empty identifier is serialized as a valid key for the key:value pair "":"baz" * * - Writer state: { "foo": ["bar" * Parameters: Identifier: "" * Value: "baz" * Serialization result: { foo: ["bar", "baz" //empty identifier is ignored since we are writing into an array and not an object. * * @param Value The value we are serializing * @param Identifier The identifier of the value, empty identifiers are ignored outside of maps of values. * @param Writer The writer the value and identifier are written into. * @param bCloseWriter When set to true the Writer will be closed after the serialization. * @return Returns true if the serialization was successful, false otherwise. */ template static bool Serialize(const typename Policy::FValue& Value, const FString& Identifier, TJsonWriter& Writer, bool bCloseWriter = true) { const TSharedRef StartingElement = MakeShared(Identifier, Value); return Serialize(StartingElement, Writer, bCloseWriter); } /** * Serialize the passed array of values into the writer. * This will effectively serialize all of the values enclosed in [] square brackets. * * Json Example: * - Writer state: [123 * Parameter: Array: ["foo", "bar", "", 456] * Serialization result: [123, ["foo", "bar", "", 456] * * @param Array The array we are serializing * @param Writer The writer the array is written into. * @param bCloseWriter When set to true the Writer will be closed after the serialization. * @return Returns true if the serialization was successful, false otherwise. */ template static bool Serialize(const typename Policy::FArrayOfValues& Array, const TSharedRef>& Writer, bool bCloseWriter = true) { return Serialize(Array, *Writer, bCloseWriter); } /** * Serialize the passed array of values into the writer. * This will effectively serialize all of the values enclosed in [] square brackets. * * Json Example: * - Writer state: [123 * Parameter: Array: ["foo", "bar", "", 456] * Serialization result: [123, ["foo", "bar", "", 456] * * @param Array The array we are serializing * @param Writer The writer the array is written into. * @param bCloseWriter When set to true the Writer will be closed after the serialization. * @return Returns true if the serialization was successful, false otherwise. */ template static bool Serialize(const typename Policy::FArrayOfValues& Array, TJsonWriter& Writer, bool bCloseWriter = true ) { const TSharedRef StartingElement = MakeShared(Array); return Serialize(StartingElement, Writer, bCloseWriter); } /** * Serialize the passed map of values into the writer. * This will effectively serialize all of the identifier:value pairs of the map, enclosed in {} curly brackets. * * Json Example: * - Writer state: [123 * Parameter: Object: {"foo": "bar", "baz": "", "": 456} * Serialization result: [123, {"foo": "bar", "baz": "", "": 456} * * @param Object The map of values we are serializing * @param Writer The writer the object is written into. * @param bCloseWriter When set to true the Writer will be closed after the serialization. * @return Returns true if the serialization was successful, false otherwise. */ template static bool Serialize(const typename Policy::FMapOfValues& Object, const TSharedRef>& Writer, bool bCloseWriter = true ) { return Serialize(Object, *Writer, bCloseWriter); } /** * Serialize the passed map of values into the writer. * This will effectively serialize all of the identifier:value pairs of the map, enclosed in {} curly brackets. * * Json Example: * - Writer state: [123 * Parameter: Object: {"foo": "bar", "baz": "", "": 456} * Serialization result: [123, {"foo": "bar", "baz": "", "": 456} * * @param Object The map of values we are serializing * @param Writer The writer the object is written into. * @param bCloseWriter When set to true the Writer will be closed after the serialization. * @return Returns true if the serialization was successful, false otherwise. */ template static bool Serialize(const typename Policy::FMapOfValues& Object, TJsonWriter& Writer, bool bCloseWriter = true) { const TSharedRef StartingElement = MakeShared(Object); return Serialize(StartingElement, Writer, bCloseWriter); } private: template static bool Deserialize(TJsonReader& Reader, typename Policy::StackState& OutStackState, EFlags InOptions) { TArray> ScopeStack; TSharedPtr CurrentState; typename Policy::FValue NewValue; EJsonNotation Notation; while (Reader.ReadNext(Notation)) { FString Identifier = Reader.GetIdentifier(); Policy::ResetValue(NewValue); bool bWasValueRead = false; switch( Notation ) { case EJsonNotation::ObjectStart: { if (CurrentState.IsValid()) { ScopeStack.Push(CurrentState.ToSharedRef()); } CurrentState = MakeShared(); CurrentState->Identifier = Identifier; Policy::ReadObjectStart(*CurrentState); } break; case EJsonNotation::ObjectEnd: { if (ScopeStack.Num() > 0) { Identifier = CurrentState->Identifier; Policy::ReadObjectEnd(*CurrentState, NewValue); bWasValueRead = true; CurrentState = ScopeStack.Pop(); } } break; case EJsonNotation::ArrayStart: { if (CurrentState.IsValid()) { ScopeStack.Push(CurrentState.ToSharedRef()); } CurrentState = MakeShared(); Policy::ReadArrayStart(*CurrentState); CurrentState->Identifier = Identifier; } break; case EJsonNotation::ArrayEnd: { if (ScopeStack.Num() > 0) { Identifier = CurrentState->Identifier; Policy::ReadArrayEnd(*CurrentState, NewValue); bWasValueRead = true; CurrentState = ScopeStack.Pop(); } } break; case EJsonNotation::Boolean: Policy::ReadBoolean(Reader, NewValue); bWasValueRead = true; break; case EJsonNotation::String: Policy::ReadString(Reader, NewValue); bWasValueRead = true; break; case EJsonNotation::Number: { if (EnumHasAnyFlags(InOptions, EFlags::StoreNumbersAsStrings)) { Policy::ReadNumberAsString(Reader, NewValue); } else { Policy::ReadNumber(Reader, NewValue); } bWasValueRead = true; } break; case EJsonNotation::Null: Policy::ReadNull(NewValue); bWasValueRead = true; break; case EJsonNotation::Error: return false; break; } if (bWasValueRead && CurrentState.IsValid()) { if (CurrentState->Type == EJson::Object) { Policy::AddValueToObject(*CurrentState, Identifier, NewValue); } else { Policy::AddValueToArray(*CurrentState, NewValue); } } } if (!CurrentState.IsValid() || !Reader.GetErrorMessage().IsEmpty()) { return false; } OutStackState = *CurrentState.Get(); return true; } template static bool Serialize(const TSharedRef& StartingElement, TJsonWriter& Writer, bool bCloseWriter) { TArray> ElementStack; ElementStack.Push(StartingElement); while (ElementStack.Num() > 0) { TSharedRef Element = ElementStack.Pop(); // Empty keys are valid identifiers only when writing inside an object. const bool bWriteValueOnly = !Element->bIsKeyValuePair || (Element->Identifier.IsEmpty() && Writer.GetCurrentElementType() != EJson::Object); if (Policy::SerializeIfBool(Element, Writer, bWriteValueOnly)) { continue; } if (Policy::SerializeIfNumber(Element, Writer, bWriteValueOnly)) { continue; } if (Policy::SerializeIfString(Element, Writer, bWriteValueOnly)) { continue; } if (Policy::SerializeIfNull(Element, Writer, bWriteValueOnly)) { continue; } if (Policy::SerializeIfArray(ElementStack, Element, Writer, bWriteValueOnly)) { continue; } if (Policy::SerializeIfObject(ElementStack, Element, Writer, bWriteValueOnly)) { continue; } UE_LOG(LogJson, Fatal, TEXT("Could not print Json Value, unrecognized type.")); } if (bCloseWriter) { return Writer.Close(); } else { return true; } } }; using FJsonSerializer = TJsonSerializer;