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

572 lines
17 KiB
C++

// 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;
class FJsonSerializer
{
public:
enum class EFlags
{
None = 0,
StoreNumbersAsStrings = 1,
};
template <class CharType>
static bool Deserialize(const TSharedRef<TJsonReader<CharType>>& Reader, TArray<TSharedPtr<FJsonValue>>& OutArray, EFlags InOptions = EFlags::None)
{
return Deserialize(*Reader, OutArray, InOptions);
}
template <class CharType>
static bool Deserialize(TJsonReader<CharType>& Reader, TArray<TSharedPtr<FJsonValue>>& OutArray, EFlags InOptions = EFlags::None)
{
StackState State;
if (!Deserialize(Reader, /*OUT*/State, InOptions))
{
return false;
}
// Empty array is ok.
if (State.Type != EJson::Array)
{
return false;
}
OutArray = State.Array;
return true;
}
template <class CharType>
static bool Deserialize(const TSharedRef<TJsonReader<CharType>>& Reader, TSharedPtr<FJsonObject>& OutObject, EFlags InOptions = EFlags::None)
{
return Deserialize(*Reader, OutObject, InOptions);
}
template <class CharType>
static bool Deserialize(TJsonReader<CharType>& Reader, TSharedPtr<FJsonObject>& OutObject, EFlags InOptions = EFlags::None)
{
StackState State;
if (!Deserialize(Reader, /*OUT*/State, InOptions))
{
return false;
}
if (!State.Object.IsValid())
{
return false;
}
OutObject = State.Object;
return true;
}
template <class CharType>
static bool Deserialize(const TSharedRef<TJsonReader<CharType>>& Reader, TSharedPtr<FJsonValue>& OutValue, EFlags InOptions = EFlags::None)
{
return Deserialize(*Reader, OutValue, InOptions);
}
template <class CharType>
static bool Deserialize(TJsonReader<CharType>& Reader, TSharedPtr<FJsonValue>& OutValue, EFlags InOptions = EFlags::None)
{
StackState State;
if (!Deserialize(Reader, /*OUT*/State, InOptions))
{
return false;
}
switch (State.Type)
{
case EJson::Object:
if (!State.Object.IsValid())
{
return false;
}
OutValue = MakeShared<FJsonValueObject>(State.Object);
break;
case EJson::Array:
OutValue = MakeShared<FJsonValueArray>(State.Array);
break;
case EJson::None:
case EJson::Null:
case EJson::String:
case EJson::Number:
case EJson::Boolean:
default:
// FIXME: would be nice to handle non-composite root values but StackState Deserialize just drops them on the floor
return false;
}
return true;
}
/**
* Serialize the passed array of json values into the writer.
* This will effectively serialize all of the values enclosed in [] square brackets.
* Example:
* - Writer state: [123 <writer position>
* Parameter: Array: ["foo", "bar", "", 456]
* Serialization result: [123, ["foo", "bar", "", 456] <writer position>
*
* @param Array The json 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 <class CharType, class PrintPolicy>
static bool Serialize(const TArray<TSharedPtr<FJsonValue>>& Array, const TSharedRef<TJsonWriter<CharType, PrintPolicy>>& Writer, bool bCloseWriter = true)
{
return Serialize(Array, *Writer, bCloseWriter);
}
/**
* Serialize the passed array of json values into the writer.
* This will effectively serialize all of the values enclosed in [] square brackets.
* Example:
* - Writer state: [123 <writer position>
* Parameter: Array: ["foo", "bar", "", 456]
* Serialization result: [123, ["foo", "bar", "", 456] <writer position>
*
* @param Array The json 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 <class CharType, class PrintPolicy>
static bool Serialize(const TArray<TSharedPtr<FJsonValue>>& Array, TJsonWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter = true )
{
const TSharedRef<FElement> StartingElement = MakeShared<FElement>(Array);
return FJsonSerializer::Serialize<CharType, PrintPolicy>(StartingElement, Writer, bCloseWriter);
}
/**
* Serialize the passed Json object into the writer.
* This will effectively serialize all of the identifier:value pairs of the object enclosed in {} curly brackets.
* Example:
* - Writer state: [123 <writer position>
* Parameter: Object: {"foo": "bar", "baz": "", "": 456}
* Serialization result: [123, {"foo": "bar", "baz": "", "": 456} <writer position>
*
* @param Object The json object 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 <class CharType, class PrintPolicy>
static bool Serialize(const TSharedRef<FJsonObject>& Object, const TSharedRef<TJsonWriter<CharType, PrintPolicy>>& Writer, bool bCloseWriter = true )
{
return Serialize(Object, *Writer, bCloseWriter);
}
/**
* Serialize the passed Json object into the writer.
* This will effectively serialize all of the identifier:value pairs of the object enclosed in {} brackets.
* Example:
* - Writer state: [123 <writer position>
* Parameter: Object: {"foo": "bar", "baz": "", "": 456}
* Serialization result: [123, {"foo": "bar", "baz": "", "": 456} <writer position>
*
* @param Object The json object 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 <class CharType, class PrintPolicy>
static bool Serialize(const TSharedRef<FJsonObject>& Object, TJsonWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter = true)
{
const TSharedRef<FElement> StartingElement = MakeShared<FElement>(Object);
return FJsonSerializer::Serialize<CharType, PrintPolicy>(StartingElement, Writer, bCloseWriter);
}
/**
* Serialize the passed Json value and identifier into the writer.
* Empty string identifiers will be ignored when the writer is not writing inside of a json object and only the value will be serialized.
* If the writer is in a state where it's currently writing inside of a json object, then the identifier will always be serialized.
* Examples:
* - Writer state: { "foo": "bar" <writer position>
* Parameters: Identifier: ""
* Value: "baz"
* Serialization result: { "foo": "bar", "": "baz" <writer position> //empty identifier is serialized as a valid key for the key:value pair "":"baz"
*
* - Writer state: { "foo": ["bar" <writer position>
* Parameters: Identifier: ""
* Value: "baz"
* Serialization result: { foo: ["bar", "baz" <writer position> //empty identifier is ignored since we are writing into an array and not an object.
*
* @param Value The json value we are serializing
* @param Identifier The identifier of the value, empty identifiers are ignored outside of json objects.
* @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 <class CharType, class PrintPolicy>
static bool Serialize(const TSharedPtr<FJsonValue>& Value, const FString& Identifier, const TSharedRef<TJsonWriter<CharType, PrintPolicy>>& Writer, bool bCloseWriter = true)
{
return Serialize(Value, Identifier, *Writer, bCloseWriter);
}
/**
* Serialize the passed Json value and identifier into the writer.
* Empty string identifiers will be ignored when the writer is not writing inside of a json object and only the value will be serialized.
* If the writer is in a state where it's currently writing inside of a json object, then the identifier will always be serialized.
* Examples:
* - Writer state: { "foo": "bar" <writer position>
* Parameters: Identifier: ""
* Value: "baz"
* Serialization result: { "foo": "bar", "": "baz" <writer position> //empty identifier is serialized as a valid key for the key:value pair "":"baz"
*
* - Writer state: { "foo": ["bar" <writer position>
* Parameters: Identifier: ""
* Value: "baz"
* Serialization result: { foo: ["bar", "baz" <writer position> //empty identifier is ignored since we are writing into an array and not an object.
*
* @param Value The json value we are serializing
* @param Identifier The identifier of the value, empty identifiers are ignored outside of json objects.
* @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 <class CharType, class PrintPolicy>
static bool Serialize(const TSharedPtr<FJsonValue>& Value, const FString& Identifier, TJsonWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter = true)
{
const TSharedRef<FElement> StartingElement = MakeShared<FElement>(Identifier, Value);
return FJsonSerializer::Serialize<CharType, PrintPolicy>(StartingElement, Writer, bCloseWriter);
}
private:
struct StackState
{
EJson Type;
FString Identifier;
TArray<TSharedPtr<FJsonValue>> Array;
TSharedPtr<FJsonObject> Object;
};
struct FElement
{
FElement( const TSharedPtr<FJsonValue>& InValue )
: Identifier()
, Value(InValue)
{ }
FElement( const TSharedRef<FJsonObject>& Object )
: Identifier()
, Value(MakeShared<FJsonValueObject>(Object))
{ }
FElement( const TArray<TSharedPtr<FJsonValue>>& Array )
: Identifier()
, Value(MakeShared<FJsonValueArray>(Array))
{ }
FElement( const FString& InIdentifier, const TSharedPtr< FJsonValue >& InValue )
: Identifier( InIdentifier )
, Value( InValue )
, bIsKeyValuePair( true )
{
}
FString Identifier;
TSharedPtr< FJsonValue > Value;
bool bHasBeenProcessed = false;
bool bIsKeyValuePair = false;
};
private:
template <class CharType>
static bool Deserialize(TJsonReader<CharType>& Reader, StackState& OutStackState, EFlags InOptions)
{
TArray<TSharedRef<StackState>> ScopeStack;
TSharedPtr<StackState> CurrentState;
TSharedPtr<FJsonValue> NewValue;
EJsonNotation Notation;
while (Reader.ReadNext(Notation))
{
FString Identifier = Reader.GetIdentifier();
NewValue.Reset();
switch( Notation )
{
case EJsonNotation::ObjectStart:
{
if (CurrentState.IsValid())
{
ScopeStack.Push(CurrentState.ToSharedRef());
}
CurrentState = MakeShared<StackState>();
CurrentState->Type = EJson::Object;
CurrentState->Identifier = Identifier;
CurrentState->Object = MakeShared<FJsonObject>();
}
break;
case EJsonNotation::ObjectEnd:
{
if (ScopeStack.Num() > 0)
{
Identifier = CurrentState->Identifier;
NewValue = MakeShared<FJsonValueObject>(CurrentState->Object);
CurrentState = ScopeStack.Pop();
}
}
break;
case EJsonNotation::ArrayStart:
{
if (CurrentState.IsValid())
{
ScopeStack.Push(CurrentState.ToSharedRef());
}
CurrentState = MakeShared<StackState>();
CurrentState->Type = EJson::Array;
CurrentState->Identifier = Identifier;
}
break;
case EJsonNotation::ArrayEnd:
{
if (ScopeStack.Num() > 0)
{
Identifier = CurrentState->Identifier;
NewValue = MakeShared<FJsonValueArray>(CurrentState->Array);
CurrentState = ScopeStack.Pop();
}
}
break;
case EJsonNotation::Boolean:
NewValue = MakeShared<FJsonValueBoolean>(Reader.GetValueAsBoolean());
break;
case EJsonNotation::String:
{
using StoredCharType = typename TJsonReader<CharType>::StoredCharType;
NewValue = MakeShared<TJsonValueString<StoredCharType>>(Reader.StealInternalValueAsString());
}
break;
case EJsonNotation::Number:
if (EnumHasAnyFlags(InOptions, EFlags::StoreNumbersAsStrings))
{
using StoredCharType = typename TJsonReader<CharType>::StoredCharType;
NewValue = MakeShared<TJsonValueNumberString<StoredCharType>>(Reader.GetValueAsNumberString());
}
else
{
NewValue = MakeShared<FJsonValueNumber>(Reader.GetValueAsNumber());
}
break;
case EJsonNotation::Null:
NewValue = MakeShared<FJsonValueNull>();
break;
case EJsonNotation::Error:
return false;
break;
}
if (NewValue.IsValid() && CurrentState.IsValid())
{
if (CurrentState->Type == EJson::Object)
{
CurrentState->Object->SetField(Identifier, NewValue);
}
else
{
CurrentState->Array.Add(NewValue);
}
}
}
if (!CurrentState.IsValid() || !Reader.GetErrorMessage().IsEmpty())
{
return false;
}
OutStackState = *CurrentState.Get();
return true;
}
template <class CharType, class PrintPolicy>
static bool Serialize(const TSharedRef<FElement>& StartingElement, TJsonWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter)
{
TArray<TSharedRef<FElement>> ElementStack;
ElementStack.Push(StartingElement);
while (ElementStack.Num() > 0)
{
TSharedRef<FElement> 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);
check(Element->Value->Type != EJson::None);
switch (Element->Value->Type)
{
case 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());
}
}
}
break;
case EJson::Boolean:
{
if (bWriteValueOnly)
{
Writer.WriteValue(Element->Value->AsBool());
}
else
{
Writer.WriteValue(Element->Identifier, Element->Value->AsBool());
}
}
break;
case EJson::String:
{
if (bWriteValueOnly)
{
Writer.WriteValue(Element->Value->AsString());
}
else
{
Writer.WriteValue(Element->Identifier, Element->Value->AsString());
}
}
break;
case EJson::Null:
{
if (bWriteValueOnly)
{
Writer.WriteNull();
}
else
{
Writer.WriteNull(Element->Identifier);
}
}
break;
case EJson::Array:
{
if (Element->bHasBeenProcessed)
{
Writer.WriteArrayEnd();
}
else
{
Element->bHasBeenProcessed = true;
ElementStack.Push(Element);
if (bWriteValueOnly)
{
Writer.WriteArrayStart();
}
else
{
Writer.WriteArrayStart(Element->Identifier);
}
TArray<TSharedPtr<FJsonValue>> Values = Element->Value->AsArray();
for (int Index = Values.Num() - 1; Index >= 0; --Index)
{
ElementStack.Push(MakeShared<FElement>(Values[Index]));
}
}
}
break;
case EJson::Object:
{
if (Element->bHasBeenProcessed)
{
Writer.WriteObjectEnd();
}
else
{
Element->bHasBeenProcessed = true;
ElementStack.Push(Element);
if (bWriteValueOnly)
{
Writer.WriteObjectStart();
}
else
{
Writer.WriteObjectStart(Element->Identifier);
}
TArray<FString> Keys;
TArray<TSharedPtr<FJsonValue>> Values;
TSharedPtr<FJsonObject> 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<FElement>(Keys[Index], Values[Index]));
}
}
}
break;
case EJson::None:
default:
UE_LOG(LogJson, Fatal,TEXT("Could not print Json Value, unrecognized type."));
}
}
if (bCloseWriter)
{
return Writer.Close();
}
else
{
return true;
}
}
};