You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
860 lines
25 KiB
C++
860 lines
25 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
JsonMessageSerializer.cpp: Implements the FJsonMessageSerializer structure.
|
|
=============================================================================*/
|
|
|
|
#include "MessagingPrivatePCH.h"
|
|
|
|
|
|
struct FJsonStructReadState
|
|
{
|
|
// Holds a pointer to the property's data.
|
|
void* Data;
|
|
|
|
// Holds the property's meta data.
|
|
UProperty* Property;
|
|
|
|
// Holds a pointer to the UStruct describing the data.
|
|
UStruct* TypeInfo;
|
|
};
|
|
|
|
struct FJsonStructWriteState
|
|
{
|
|
// Holds a pointer to the property's data.
|
|
const void* Data;
|
|
|
|
// Holds a flag indicating whether the property has been processed.
|
|
bool HasBeenProcessed;
|
|
|
|
// Holds the property's meta data.
|
|
UProperty* Property;
|
|
|
|
// Holds a pointer to the UStruct describing the data.
|
|
UStruct* TypeInfo;
|
|
};
|
|
|
|
|
|
// @todo gmp - refactor this and SlateStyleSerializer into generic UStruct serializer
|
|
namespace Obsolete
|
|
{
|
|
// Finds the class for the given stack state.
|
|
UStruct* FindClass( const FJsonStructReadState& State )
|
|
{
|
|
UStruct* Class = nullptr;
|
|
|
|
if (State.Property != nullptr)
|
|
{
|
|
UProperty* ParentProperty = State.Property;
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(ParentProperty);
|
|
|
|
if (ArrayProperty != nullptr)
|
|
{
|
|
ParentProperty = ArrayProperty->Inner;
|
|
}
|
|
|
|
UStructProperty* StructProperty = Cast<UStructProperty>(ParentProperty);
|
|
UObjectPropertyBase* ObjectProperty = Cast<UObjectPropertyBase>(ParentProperty);
|
|
|
|
if (StructProperty != nullptr)
|
|
{
|
|
Class = StructProperty->Struct;
|
|
}
|
|
else if (ObjectProperty != nullptr)
|
|
{
|
|
Class = ObjectProperty->PropertyClass;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UObject* RootObject = static_cast<UObject*>(State.Data);
|
|
Class = RootObject->GetClass();
|
|
}
|
|
|
|
return Class;
|
|
}
|
|
|
|
|
|
// Finds the specified property for the given stack state.
|
|
UProperty* FindProperty( const FJsonStructReadState& State, const FString& Identifier )
|
|
{
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>( State.Property );
|
|
if ( ArrayProperty )
|
|
{
|
|
return ArrayProperty->Inner;
|
|
}
|
|
|
|
return FindField<UProperty>( State.TypeInfo, *Identifier );
|
|
}
|
|
|
|
|
|
// Gets the value from the given property.
|
|
template< typename UPropertyType, typename PropertyType >
|
|
PropertyType* GetPropertyValue( const FJsonStructWriteState& State, UProperty* Property )
|
|
{
|
|
PropertyType* ValuePtr = nullptr;
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(State.Property);
|
|
|
|
if (ArrayProperty)
|
|
{
|
|
check(ArrayProperty->Inner == Property);
|
|
|
|
FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->template ContainerPtrToValuePtr<void>(State.Data));
|
|
int32 Index = ArrayHelper.AddValue();
|
|
|
|
ValuePtr = (PropertyType*)ArrayHelper.GetRawPtr( Index );
|
|
}
|
|
else
|
|
{
|
|
UPropertyType* TypedProperty = Cast<UPropertyType>( Property );
|
|
check( TypedProperty )
|
|
|
|
ValuePtr = TypedProperty->template ContainerPtrToValuePtr<PropertyType>(State.Data);
|
|
}
|
|
|
|
return ValuePtr;
|
|
}
|
|
|
|
|
|
// Gets the value from the given property.
|
|
template< typename UPropertyType, typename PropertyType >
|
|
PropertyType* GetPropertyValue( const FJsonStructReadState& State, UProperty* Property )
|
|
{
|
|
PropertyType* ValuePtr = nullptr;
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(State.Property);
|
|
|
|
if (ArrayProperty)
|
|
{
|
|
check(ArrayProperty->Inner == Property);
|
|
|
|
FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->template ContainerPtrToValuePtr<void>(State.Data));
|
|
int32 Index = ArrayHelper.AddValue();
|
|
|
|
ValuePtr = (PropertyType*)ArrayHelper.GetRawPtr( Index );
|
|
}
|
|
else
|
|
{
|
|
UPropertyType* TypedProperty = Cast<UPropertyType>( Property );
|
|
check( TypedProperty )
|
|
|
|
ValuePtr = TypedProperty->template ContainerPtrToValuePtr<PropertyType>(State.Data);
|
|
}
|
|
|
|
return ValuePtr;
|
|
}
|
|
|
|
|
|
// Sets the value of the given property.
|
|
template< typename UPropertyType, typename PropertyType >
|
|
void SetPropertyValue( const FJsonStructReadState& State, UProperty* Property, const PropertyType& Value )
|
|
{
|
|
PropertyType* ValuePtr = GetPropertyValue<UPropertyType, PropertyType>(State , Property);
|
|
*ValuePtr = Value;
|
|
}
|
|
|
|
|
|
bool ReadExpectedArrayValue( const TSharedRef< TJsonReader<UCS2CHAR> >& Reader, const EJsonNotation::Type ExpectNotation, const FString& ErrorMessage )
|
|
{
|
|
bool ReadCorrectValue = true;
|
|
|
|
EJsonNotation::Type Notation;
|
|
if ( !Reader->ReadNext( Notation ) || Notation != ExpectNotation )
|
|
{
|
|
ReadCorrectValue = false;
|
|
|
|
UE_LOG( LogJson, Log, TEXT("%s Line: %u Ch: %u"), *ErrorMessage, Reader->GetLineNumber(), Reader->GetCharacterNumber() );
|
|
|
|
if ( Notation != EJsonNotation::ArrayEnd )
|
|
{
|
|
Reader->SkipArray();
|
|
}
|
|
}
|
|
|
|
return ReadCorrectValue;
|
|
}
|
|
|
|
|
|
// Writes a property value to the serialization output. Array elements will be written without their name.
|
|
template<typename ValueType>
|
|
void SerializeProperty( const TSharedRef<TJsonWriter<UCS2CHAR, TPrettyJsonPrintPolicy<UCS2CHAR> > > Writer, UProperty* Property, const ValueType& Value)
|
|
{
|
|
if ((Property == nullptr) || (Property->GetOuter()->GetClass() == UArrayProperty::StaticClass()))
|
|
{
|
|
Writer->WriteValue(Value);
|
|
}
|
|
else
|
|
{
|
|
Writer->WriteValue(Property->GetName(), Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* FJsonMessageSerializer implementation
|
|
*****************************************************************************/
|
|
|
|
bool FJsonMessageSerializer::DeserializeMessage( FArchive& Archive, IMutableMessageContextRef& OutContext )
|
|
{
|
|
// deserialize context
|
|
FMessageAddress Sender;
|
|
Archive << Sender;
|
|
OutContext->SetSender(Sender);
|
|
|
|
TArray<FMessageAddress> Recipients;
|
|
Archive << Recipients;
|
|
|
|
for (int32 RecipientIndex = 0; RecipientIndex < Recipients.Num(); ++RecipientIndex)
|
|
{
|
|
OutContext->AddRecipient(Recipients[RecipientIndex]);
|
|
}
|
|
|
|
TEnumAsByte<EMessageScope::Type> Scope;
|
|
Archive << Scope;
|
|
OutContext->SetScope(Scope);
|
|
|
|
FDateTime TimeSent;
|
|
Archive << TimeSent;
|
|
OutContext->SetTimeSent(TimeSent);
|
|
|
|
FDateTime Expiration;
|
|
Archive << Expiration;
|
|
OutContext->SetExpiration(Expiration);
|
|
|
|
TMap<FName, FString> Headers;
|
|
Archive << Headers;
|
|
|
|
for (TMap<FName, FString>::TConstIterator It(Headers); It; ++It)
|
|
{
|
|
OutContext->SetHeader(It.Key(), It.Value());
|
|
}
|
|
|
|
// serialize message
|
|
TSharedRef<TJsonReader<UCS2CHAR> > Reader = TJsonReader<UCS2CHAR>::Create(&Archive);
|
|
|
|
return DeserializeStruct(OutContext, Reader);
|
|
}
|
|
|
|
|
|
bool FJsonMessageSerializer::DeserializeStruct( IMutableMessageContextRef& OutContext, const TSharedRef<TJsonReader<UCS2CHAR> >& Reader ) const
|
|
{
|
|
bool bFirstObjectStartNotation = true;
|
|
|
|
TArray<FJsonStructReadState> StateStack;
|
|
FJsonStructReadState CurrentState;
|
|
|
|
CurrentState.Data = nullptr;
|
|
CurrentState.Property = nullptr;
|
|
CurrentState.TypeInfo = nullptr;
|
|
|
|
EJsonNotation::Type Notation;
|
|
|
|
while (Reader->ReadNext(Notation))
|
|
{
|
|
FString Identifier = Reader->GetIdentifier();
|
|
|
|
switch (Notation)
|
|
{
|
|
// arrays
|
|
case EJsonNotation::ArrayEnd:
|
|
{
|
|
CurrentState = StateStack.Pop();
|
|
}
|
|
|
|
break;
|
|
|
|
case EJsonNotation::ArrayStart:
|
|
{
|
|
check( bFirstObjectStartNotation == false );
|
|
StateStack.Push( CurrentState );
|
|
|
|
FJsonStructReadState NewState;
|
|
NewState.Data = CurrentState.Data;
|
|
|
|
//TODO: Handle arrays of arrays
|
|
// This does not handle arrays of arrays, merely arrays of objects or structures
|
|
NewState.Property = FindField<UProperty>( CurrentState.TypeInfo, *Identifier );
|
|
|
|
if ( NewState.Property == nullptr )
|
|
{
|
|
CurrentState = StateStack.Pop();
|
|
Reader->SkipArray();
|
|
|
|
UE_LOG( LogJson, Verbose, TEXT("Array member '%s' was unhandled. Line: %u Ch: %u"), *Identifier, Reader->GetLineNumber(), Reader->GetCharacterNumber() );
|
|
}
|
|
else
|
|
{
|
|
NewState.TypeInfo = Obsolete::FindClass( NewState );
|
|
CurrentState = NewState;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// booleans
|
|
case EJsonNotation::Boolean:
|
|
{
|
|
UProperty* Property = Obsolete::FindProperty(CurrentState, *Identifier);
|
|
|
|
if (Property == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("The property %s does not exist (Line: %u, Ch: %u)"), *Identifier, Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
else
|
|
{
|
|
bool BoolValue = Reader->GetValueAsBoolean();
|
|
|
|
if (Property->GetClass() == UBoolProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UBoolProperty, bool>(CurrentState, Property, BoolValue);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("Boolean field %s with value '%s' is not supported in UProperty type %s (Line: %u, Ch: %u)"), *Identifier, BoolValue ? *(GTrue.ToString()) : *(GFalse.ToString()), *Property->GetClass()->GetName(), Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// numeric values
|
|
case EJsonNotation::Number:
|
|
{
|
|
UProperty* Property = Obsolete::FindProperty(CurrentState, *Identifier);
|
|
|
|
if (Property == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("The property %s does not exist (Line: %u, Ch: %u)"), *Identifier, Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
else
|
|
{
|
|
float NumericValue = Reader->GetValueAsNumber();
|
|
|
|
if (Property->GetClass() == UByteProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UByteProperty, int8>( CurrentState, Property, (int8)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UFloatProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UFloatProperty, float>(CurrentState, Property, NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UDoubleProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UDoubleProperty, double>(CurrentState, Property, NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UIntProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UIntProperty, int32>( CurrentState, Property, (int32)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UUInt32Property::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UUInt32Property, uint32>( CurrentState, Property, (uint32)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UInt16Property::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UInt16Property, int16>( CurrentState, Property, (int16)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UUInt16Property::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UUInt16Property, uint16>( CurrentState, Property, (uint16)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UInt64Property::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UInt64Property, int64>( CurrentState, Property, (int64)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UUInt64Property::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UUInt64Property, uint64>( CurrentState, Property, (uint64)NumericValue);
|
|
}
|
|
else if (Property->GetClass() == UInt8Property::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UInt8Property, int8>( CurrentState, Property, (int8)NumericValue);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("Numeric field %s with value '%f' is not supported in UProperty type %s (Line: %u, Ch: %u)"), *Identifier, NumericValue, *Property->GetClass()->GetName(), Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// objects & structures
|
|
case EJsonNotation::ObjectEnd:
|
|
{
|
|
if (StateStack.Num() > 0)
|
|
{
|
|
CurrentState = StateStack.Pop();
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case EJsonNotation::ObjectStart:
|
|
{
|
|
if (bFirstObjectStartNotation)
|
|
{
|
|
if (Reader->ReadNext(Notation) && (Notation == EJsonNotation::String) && (Reader->GetIdentifier() == TEXT("__type")))
|
|
{
|
|
const FString& TypeName = Reader->GetValueAsString();
|
|
UScriptStruct* ScriptStruct = FMessageTypeMap::MessageTypeMap.Find(*TypeName);
|
|
|
|
if (ScriptStruct != nullptr)
|
|
{
|
|
CurrentState.Data = FMemory::Malloc(ScriptStruct->PropertiesSize);
|
|
CurrentState.Property = nullptr;
|
|
CurrentState.TypeInfo = ScriptStruct;
|
|
|
|
ScriptStruct->InitializeScriptStruct(CurrentState.Data);
|
|
|
|
bFirstObjectStartNotation = false;
|
|
}
|
|
else
|
|
{
|
|
//UE_LOG(LogJson, Verbose, TEXT("The message type %s could not be found"), *TypeName);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("Invalid Json object"));
|
|
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StateStack.Push(CurrentState);
|
|
|
|
FJsonStructReadState NewState;
|
|
|
|
if (Identifier.IsEmpty())
|
|
{
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>( CurrentState.Property );
|
|
|
|
check(ArrayProperty);
|
|
|
|
FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr<void>(CurrentState.Data));
|
|
const int32 ArrayIndex = ArrayHelper.AddValue();
|
|
NewState.Property = CurrentState.Property;
|
|
NewState.Data = ArrayHelper.GetRawPtr(ArrayIndex);
|
|
}
|
|
else
|
|
{
|
|
NewState.Property = FindField<UProperty>(CurrentState.TypeInfo, *Identifier );
|
|
|
|
if (NewState.Property != nullptr)
|
|
{
|
|
NewState.Data = NewState.Property->ContainerPtrToValuePtr<void>(CurrentState.Data);
|
|
}
|
|
}
|
|
|
|
if (NewState.Property == nullptr)
|
|
{
|
|
CurrentState = StateStack.Pop();
|
|
Reader->SkipObject();
|
|
|
|
UE_LOG(LogJson, Verbose, TEXT("Json object member '%s' was unhandled. Line: %u Ch: %u"), *Identifier, Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
else
|
|
{
|
|
NewState.TypeInfo = Obsolete::FindClass(NewState);
|
|
CurrentState = NewState;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// strings, names & enumerations
|
|
case EJsonNotation::String:
|
|
{
|
|
UProperty* Property = Obsolete::FindProperty(CurrentState, *Identifier);
|
|
|
|
if (Property == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("The property %s does not exist (Line: %u, Ch: %u)"), *Identifier, Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
else
|
|
{
|
|
const FString& StringValue = Reader->GetValueAsString();
|
|
|
|
if (Property->GetClass() == UStrProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UStrProperty, FString>(CurrentState, Property, StringValue);
|
|
}
|
|
else if (Property->GetClass() == UNameProperty::StaticClass())
|
|
{
|
|
Obsolete::SetPropertyValue<UNameProperty, FName>(CurrentState, Property, *StringValue);
|
|
}
|
|
else if (Property->GetClass() == UByteProperty::StaticClass())
|
|
{
|
|
UByteProperty* ByteProperty = Cast<UByteProperty>(Property);
|
|
int32 Index = ByteProperty->Enum->FindEnumIndex(*StringValue);
|
|
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
Obsolete::SetPropertyValue<UByteProperty, uint8>(CurrentState, Property, (uint8)Index);
|
|
}
|
|
}
|
|
else if (Property->GetClass() == UArrayProperty::StaticClass())
|
|
{
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property);
|
|
|
|
if (ArrayProperty->Inner->GetClass() == UByteProperty::StaticClass())
|
|
{
|
|
int32 ArrayNum = StringValue.Len() / 3;
|
|
|
|
if (ArrayNum > 0)
|
|
{
|
|
FScriptArrayHelper_InContainer Helper(ArrayProperty, CurrentState.Data);
|
|
Helper.EmptyAndAddValues(ArrayNum);
|
|
|
|
FString::ToBlob(StringValue, Helper.GetRawPtr(), ArrayNum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// strings can only be decoded into byte arrays
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UStructProperty* StructProperty = Cast<UStructProperty>(Property);
|
|
|
|
if ((StructProperty != nullptr) && (StructProperty->Struct->GetName() == TEXT("Guid")))
|
|
{
|
|
FGuid ParsedGuid;
|
|
|
|
if (FGuid::Parse(StringValue, ParsedGuid))
|
|
{
|
|
FGuid* Guid = StructProperty->ContainerPtrToValuePtr<FGuid>((void*)CurrentState.Data);
|
|
*Guid = ParsedGuid;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("GUID field %s has a malformed value '%s'"), *Identifier, *StringValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("String field %s with value '%s' is not supported in UProperty type %s (Line: %u, Ch: %u)"), *Identifier, *StringValue, *Property->GetClass()->GetName(), Reader->GetLineNumber(), Reader->GetCharacterNumber());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// null values & errors
|
|
case EJsonNotation::Error:
|
|
{
|
|
UE_LOG(LogJson, Verbose, TEXT("Format error: %s"), *Reader->GetErrorMessage());
|
|
|
|
return false;
|
|
}
|
|
|
|
case EJsonNotation::Null:
|
|
{
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!Reader->GetErrorMessage().IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OutContext->SetMessage(CurrentState.Data, (UScriptStruct*)CurrentState.TypeInfo);
|
|
|
|
return (CurrentState.Data != nullptr);
|
|
}
|
|
|
|
|
|
bool FJsonMessageSerializer::SerializeMessage( const IMessageContextRef& Context, FArchive& Archive )
|
|
{
|
|
if (Context->IsValid())
|
|
{
|
|
// serialize context
|
|
FMessageAddress Sender = Context->GetSender();
|
|
Archive << Sender;
|
|
|
|
TArray<FMessageAddress> Recipients = Context->GetRecipients();
|
|
Archive << Recipients;
|
|
|
|
TEnumAsByte<EMessageScope::Type> Scope = Context->GetScope();
|
|
Archive << Scope;
|
|
|
|
FDateTime TimeSent = Context->GetTimeSent();
|
|
Archive << TimeSent;
|
|
|
|
FDateTime Expiration = Context->GetExpiration();
|
|
Archive << Expiration;
|
|
|
|
TMap<FName, FString> Headers = Context->GetHeaders();
|
|
Archive << Headers;
|
|
|
|
// serialize message
|
|
TSharedRef<TJsonWriter<UCS2CHAR> > Writer = TJsonWriter<UCS2CHAR>::Create(&Archive);
|
|
|
|
return SerializeStruct(Context->GetMessage(), *Context->GetMessageTypeInfo(), Writer);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool FJsonMessageSerializer::SerializeStruct( const void* Data, UStruct& TypeInfo, const TSharedRef<TJsonWriter<UCS2CHAR> >& Writer )
|
|
{
|
|
// initialize the state stack
|
|
TArray<FJsonStructWriteState> StateStack;
|
|
FJsonStructWriteState StructState;
|
|
|
|
StructState.Data = Data;
|
|
StructState.Property = nullptr;
|
|
StructState.TypeInfo = &TypeInfo;
|
|
StructState.HasBeenProcessed = false;
|
|
|
|
StateStack.Push(StructState);
|
|
|
|
// process state stack
|
|
while (StateStack.Num() > 0)
|
|
{
|
|
FJsonStructWriteState CurrentState = StateStack.Pop();
|
|
|
|
const void* CurrentData = CurrentState.Data;
|
|
UProperty* CurrentProperty = CurrentState.Property;
|
|
UStruct* CurrentTypeInfo = CurrentState.TypeInfo;
|
|
|
|
// booleans
|
|
if (CurrentTypeInfo == UBoolProperty::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, Cast<UBoolProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
|
|
// bytes & enumerations
|
|
else if (CurrentTypeInfo == UByteProperty::StaticClass())
|
|
{
|
|
UByteProperty* ByteProperty = Cast<UByteProperty>(CurrentProperty);
|
|
|
|
if (ByteProperty->IsEnum())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, ByteProperty->Enum->GetEnumName(ByteProperty->GetPropertyValue_InContainer(CurrentData)));
|
|
}
|
|
else
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UByteProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
}
|
|
|
|
// floats
|
|
else if (CurrentTypeInfo == UFloatProperty::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, Cast<UFloatProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
else if (CurrentTypeInfo == UDoubleProperty::StaticClass())
|
|
{
|
|
double value = Cast<UDoubleProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData);
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, value);
|
|
}
|
|
|
|
// signed integers
|
|
else if (CurrentTypeInfo == UIntProperty::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UIntProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
else if (CurrentTypeInfo == UInt8Property::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UInt8Property>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
else if (CurrentTypeInfo == UInt16Property::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UInt16Property>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
else if (CurrentTypeInfo == UInt64Property::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UInt64Property>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
|
|
// unsigned integers
|
|
else if (CurrentTypeInfo == UUInt16Property::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UUInt16Property>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
else if (CurrentTypeInfo == UUInt32Property::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UUInt32Property>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
else if (CurrentTypeInfo == UUInt64Property::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, (double)Cast<UUInt64Property>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
|
|
// names & strings
|
|
else if (CurrentTypeInfo == UNameProperty::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, Cast<UNameProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData).ToString());
|
|
}
|
|
else if (CurrentTypeInfo == UStrProperty::StaticClass())
|
|
{
|
|
Obsolete::SerializeProperty(Writer, CurrentProperty, Cast<UStrProperty>(CurrentProperty)->GetPropertyValue_InContainer(CurrentData));
|
|
}
|
|
|
|
// single & multi-dimensional arrays
|
|
else if (CurrentTypeInfo == UArrayProperty::StaticClass())
|
|
{
|
|
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(CurrentProperty);
|
|
UProperty* Inner = ArrayProperty->Inner;
|
|
|
|
if (Inner->GetClass() == UInt8Property::StaticClass() || Inner->GetClass() == UByteProperty::StaticClass())
|
|
{
|
|
const TArray<uint8>* ArrayPtr = ArrayProperty->ContainerPtrToValuePtr<TArray<uint8> >(CurrentData);
|
|
const uint8* Buffer = ArrayPtr->GetData();
|
|
UObject* Outer = CurrentProperty->GetOuter();
|
|
|
|
if ((Outer != nullptr) && (Outer->GetClass() == UArrayProperty::StaticClass()))
|
|
{
|
|
Writer->WriteValue(FString::FromBlob(Buffer, ArrayPtr->Num()));
|
|
}
|
|
else
|
|
{
|
|
Writer->WriteValue(CurrentProperty->GetName(), FString::FromBlob(Buffer, ArrayPtr->Num()));
|
|
}
|
|
}
|
|
else if (CurrentState.HasBeenProcessed)
|
|
{
|
|
Writer->WriteArrayEnd();
|
|
}
|
|
else
|
|
{
|
|
// write array start
|
|
UObject* Outer = CurrentProperty->GetOuter();
|
|
|
|
if ((Outer != nullptr) && (Outer->GetClass() == UArrayProperty::StaticClass()))
|
|
{
|
|
Writer->WriteArrayStart();
|
|
}
|
|
else
|
|
{
|
|
Writer->WriteArrayStart(CurrentProperty->GetName());
|
|
}
|
|
|
|
CurrentState.HasBeenProcessed = true;
|
|
StateStack.Push(CurrentState);
|
|
|
|
// serialize elements
|
|
FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr<void>(CurrentData));
|
|
|
|
for (int Index = ArrayHelper.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
FJsonStructWriteState ElementState;
|
|
|
|
ElementState.Data = ArrayHelper.GetRawPtr(Index);
|
|
ElementState.Property = Inner;
|
|
ElementState.TypeInfo = Inner->GetClass();
|
|
ElementState.HasBeenProcessed = false;
|
|
|
|
StateStack.Push(ElementState);
|
|
}
|
|
}
|
|
}
|
|
|
|
// objects & structures
|
|
else
|
|
{
|
|
if (CurrentState.HasBeenProcessed)
|
|
{
|
|
Writer->WriteObjectEnd();
|
|
}
|
|
else
|
|
{
|
|
const void* NewData = CurrentData;
|
|
|
|
// write object start
|
|
if (CurrentProperty == nullptr)
|
|
{
|
|
Writer->WriteObjectStart();
|
|
Writer->WriteValue(TEXT("__type"), CurrentTypeInfo->GetFName().ToString());
|
|
}
|
|
else
|
|
{
|
|
UStructProperty* StructProperty = Cast<UStructProperty>(CurrentProperty);
|
|
|
|
if ((StructProperty != nullptr) && (StructProperty->Struct->GetName() == TEXT("Guid")))
|
|
{
|
|
const FGuid* Guid = StructProperty->ContainerPtrToValuePtr<FGuid>(CurrentData);
|
|
Writer->WriteValue(StructProperty->GetName(), Guid->ToString(EGuidFormats::DigitsWithHyphens));
|
|
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
UObject* Outer = CurrentProperty->GetOuter();
|
|
|
|
if ((Outer != nullptr) && (Outer->GetClass() == UArrayProperty::StaticClass()))
|
|
{
|
|
Writer->WriteObjectStart();
|
|
}
|
|
else
|
|
{
|
|
Writer->WriteObjectStart(CurrentProperty->GetName());
|
|
NewData = CurrentProperty->ContainerPtrToValuePtr<void>(CurrentData);
|
|
}
|
|
}
|
|
}
|
|
|
|
CurrentState.HasBeenProcessed = true;
|
|
StateStack.Push(CurrentState);
|
|
|
|
// serialize fields
|
|
if (CurrentProperty != nullptr)
|
|
{
|
|
UStructProperty* StructProperty = Cast<UStructProperty>(CurrentProperty);
|
|
|
|
// @todo json: allow for custom serialization of specific types
|
|
|
|
if (StructProperty != nullptr)
|
|
{
|
|
CurrentTypeInfo = StructProperty->Struct;
|
|
}
|
|
else
|
|
{
|
|
UObjectPropertyBase* ObjectProperty = Cast<UObjectPropertyBase>(CurrentProperty);
|
|
|
|
if (ObjectProperty != nullptr)
|
|
{
|
|
CurrentTypeInfo = ObjectProperty->PropertyClass;
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray<FJsonStructWriteState> FieldStates;
|
|
|
|
for (TFieldIterator<UProperty> It(CurrentTypeInfo, EFieldIteratorFlags::IncludeSuper); It; ++It)
|
|
{
|
|
FJsonStructWriteState FieldState;
|
|
|
|
FieldState.Data = NewData;
|
|
FieldState.Property = *It;
|
|
FieldState.TypeInfo = It->GetClass();
|
|
FieldState.HasBeenProcessed = false;
|
|
|
|
FieldStates.Add(FieldState);
|
|
}
|
|
|
|
// push backwards for readability
|
|
for (int Index = FieldStates.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
StateStack.Push(FieldStates[Index]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Writer->Close();
|
|
}
|