You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Please note that file comments had no purpose in nearly all cases and just added visual clutter. The two files that had meaningful file comments had their comments moved into the corresponding classes. There are still hundreds of file comments left in other files that will be removed over time. Also cleaned up some random stuff along the way: - relative paths to public headers within the same module are no longer necessary (automatically discovered by UBT now) - header guards are deprecated, use #pragma once instead (all compilers support it now) - space between multiple template brackets is no longer required (all compilers support >> now) - NULL to nullptr, OVERRIDE to override - spelling errors, whitespace, line breaks [CL 2104067 by Max Preussner in Main branch]
856 lines
25 KiB
C++
856 lines
25 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#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();
|
|
}
|