You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
980 lines
37 KiB
980 lines
37 KiB
// Copyright 2022 Danyang Chen https://github.com/DAN-AND-DNA
|
|
|
|
#include "Struct2JsonLibrary.h"
|
|
|
|
#include "Engine/UserDefinedEnum.h"
|
|
#include "Runtime/JsonUtilities/Public/JsonObjectConverter.h"
|
|
#include "Runtime/Json/Public/Policies/PrettyJsonPrintPolicy.h"
|
|
#include "Runtime/CoreUObject/Public/UObject/TextProperty.h"
|
|
#include "Runtime/Json/Public/Policies/CondensedJsonPrintPolicy.h"
|
|
#include "Runtime/CoreUObject/Public/UObject/UObjectGlobals.h"
|
|
|
|
namespace {
|
|
bool UStructToJsonAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonValue> >& OutJsonAttributes, int64 CheckFlags = 0, int64 SkipFlags = 0, const FJsonObjectConverter::CustomExportCallback* ExportCb = nullptr, bool bOmitEmpty = false);
|
|
bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
|
|
bool JsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
|
|
bool ConvertScalarJsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
|
|
TSharedPtr<FJsonValue> UPropertyToJsonValue(FProperty* Property, const void* Value, int64 CheckFlags = 0, int64 SkipFlags = 0, const FJsonObjectConverter::CustomExportCallback* ExportCb = nullptr, FProperty* OuterProperty = nullptr, bool bOmitEmpty = false);
|
|
TSharedPtr<FJsonValue> ConvertScalarFPropertyToJsonValue(FProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, FProperty* OuterProperty, bool bOmitEmpty);
|
|
bool UStructToJsonObject(const UStruct* StructDefinition, const void* Struct, TSharedRef<FJsonObject> OutJsonObject, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, bool bOmitEmpty);
|
|
const FString ObjectClassNameKey = "_ClassName";
|
|
}
|
|
|
|
|
|
FString GetShortName(FProperty* Property) {
|
|
FString PropertyName = Property->GetName();
|
|
|
|
if (PropertyName.IsEmpty()) {
|
|
return PropertyName;
|
|
}
|
|
|
|
FString Left;
|
|
FString Right;
|
|
FString Garbage;
|
|
FString Final;
|
|
|
|
if (PropertyName.Reverse().Split("_", &Left, &Right)) {
|
|
if (Left.Len() == 32) {
|
|
Right.Split("_", &Garbage, &Final);
|
|
return Final.Reverse();
|
|
}
|
|
else
|
|
return PropertyName;
|
|
}
|
|
|
|
return PropertyName;
|
|
}
|
|
|
|
bool UStruct2JsonLibaray::JsonStr2Struct(FStructProperty* StructProperty, void* StructPtr, const FString& InJsonStr) {
|
|
UScriptStruct* Struct = StructProperty->Struct;
|
|
|
|
TSharedPtr<FJsonObject> JsonObject;
|
|
TSharedRef<TJsonReader<> > JsonReader = TJsonReaderFactory<>::Create(InJsonStr);
|
|
if (!FJsonSerializer::Deserialize(JsonReader, JsonObject) || !JsonObject.IsValid())
|
|
{
|
|
UE_LOG(LogJson, Warning, TEXT("JsonObjectStringToUStruct - Unable to parse json=[%s]"), *InJsonStr);
|
|
return false;
|
|
}
|
|
if (!::JsonAttributesToUStructWithContainer(JsonObject->Values, Struct, StructPtr, Struct, StructPtr, 1, 0))
|
|
{
|
|
UE_LOG(LogJson, Warning, TEXT("JsonObjectStringToUStruct - Unable to deserialize. json=[%s]"), *InJsonStr);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<class CharType, class PrintPolicy>
|
|
bool UStructToJsonObjectStringInternal(const TSharedRef<FJsonObject>& JsonObject, FString& OutJsonString, int32 Indent)
|
|
{
|
|
TSharedRef<TJsonWriter<CharType, PrintPolicy> > JsonWriter = TJsonWriterFactory<CharType, PrintPolicy>::Create(&OutJsonString, Indent);
|
|
bool bSuccess = FJsonSerializer::Serialize(JsonObject, JsonWriter);
|
|
JsonWriter->Close();
|
|
return bSuccess;
|
|
}
|
|
|
|
bool UStruct2JsonLibaray::Struct2JsonStr(FStructProperty* StructProperty, void* StructPtr, FString& OutJsonStr, bool bPretty, bool bOmitEmpty) {
|
|
UScriptStruct* Struct = StructProperty->Struct;
|
|
|
|
TSharedRef<FJsonObject> JsonObject = MakeShared<FJsonObject>();
|
|
// ��struct���json value
|
|
if (::UStructToJsonAttributes(Struct, StructPtr, JsonObject->Values, 1, 0, nullptr, bOmitEmpty)) {
|
|
bool bSuccess = false;
|
|
if(bPretty) {
|
|
bSuccess = UStructToJsonObjectStringInternal<TCHAR, TPrettyJsonPrintPolicy<TCHAR> >(JsonObject, OutJsonStr, 0);
|
|
} else {
|
|
bSuccess = UStructToJsonObjectStringInternal<TCHAR, TCondensedJsonPrintPolicy<TCHAR> >(JsonObject, OutJsonStr, 0);
|
|
}
|
|
|
|
if (bSuccess) {
|
|
return true;
|
|
}
|
|
else {
|
|
UE_LOG(LogJson, Warning, TEXT("UStructToJsonObjectString - Unable to write out json"));
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
#if (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 26 || ENGINE_MAJOR_VERSION > 4)
|
|
namespace {
|
|
bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
|
|
{
|
|
if (StructDefinition == FJsonObjectWrapper::StaticStruct())
|
|
{
|
|
// Just copy it into the object
|
|
FJsonObjectWrapper* ProxyObject = (FJsonObjectWrapper*)OutStruct;
|
|
ProxyObject->JsonObject = MakeShared<FJsonObject>();
|
|
ProxyObject->JsonObject->Values = JsonAttributes;
|
|
return true;
|
|
}
|
|
|
|
int32 NumUnclaimedProperties = JsonAttributes.Num();
|
|
if (NumUnclaimedProperties <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// iterate over the struct properties
|
|
for (TFieldIterator<FProperty> PropIt(StructDefinition); PropIt; ++PropIt)
|
|
{
|
|
FProperty* Property = *PropIt;
|
|
|
|
// Check to see if we should ignore this property
|
|
if (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags))
|
|
{
|
|
continue;
|
|
}
|
|
if (Property->HasAnyPropertyFlags(SkipFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// find a json value matching this property name
|
|
|
|
//const TSharedPtr<FJsonValue>* JsonValue = JsonAttributes.Find(Property->GetName());
|
|
const TSharedPtr<FJsonValue>* JsonValue = JsonAttributes.Find(GetShortName(Property)); // TEXT(L"ID_6_A21DA3F94C77867B6CA8E290E62E467E") but we just need TEXT(L"ID")
|
|
if (!JsonValue)
|
|
{
|
|
// we allow values to not be found since this mirrors the typical UObject mantra that all the fields are optional when deserializing
|
|
continue;
|
|
}
|
|
|
|
if (JsonValue->IsValid() && !(*JsonValue)->IsNull())
|
|
{
|
|
void* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);
|
|
if (!JsonValueToFPropertyWithContainer(*JsonValue, Property, Value, ContainerStruct, Container, CheckFlags, SkipFlags))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonObjectToUStruct - Unable to parse %s.%s from JSON"), *StructDefinition->GetName(), *Property->GetName());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (--NumUnclaimedProperties <= 0)
|
|
{
|
|
// If we found all properties that were in the JsonAttributes map, there is no reason to keep looking for more.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool JsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
|
|
{
|
|
if (!JsonValue.IsValid())
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Invalid value JSON key"));
|
|
return false;
|
|
}
|
|
|
|
bool bArrayOrSetProperty = Property->IsA<FArrayProperty>() || Property->IsA<FSetProperty>();
|
|
bool bJsonArray = JsonValue->Type == EJson::Array;
|
|
|
|
if (!bJsonArray)
|
|
{
|
|
if (bArrayOrSetProperty)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import TArray from non-array JSON key"));
|
|
return false;
|
|
}
|
|
|
|
if (Property->ArrayDim != 1)
|
|
{
|
|
UE_LOG(LogJson, Warning, TEXT("Ignoring excess properties when deserializing %s"), *Property->GetName());
|
|
}
|
|
|
|
return ConvertScalarJsonValueToFPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
|
|
}
|
|
|
|
// In practice, the ArrayDim == 1 check ought to be redundant, since nested arrays of FPropertys are not supported
|
|
if (bArrayOrSetProperty && Property->ArrayDim == 1)
|
|
{
|
|
// Read into TArray
|
|
return ConvertScalarJsonValueToFPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
|
|
}
|
|
|
|
// We're deserializing a JSON array
|
|
const auto& ArrayValue = JsonValue->AsArray();
|
|
if (Property->ArrayDim < ArrayValue.Num())
|
|
{
|
|
UE_LOG(LogJson, Warning, TEXT("Ignoring excess properties when deserializing %s"), *Property->GetName());
|
|
}
|
|
|
|
// Read into native array
|
|
int ItemsToRead = FMath::Clamp(ArrayValue.Num(), 0, Property->ArrayDim);
|
|
for (int Index = 0; Index != ItemsToRead; ++Index)
|
|
{
|
|
if (!ConvertScalarJsonValueToFPropertyWithContainer(ArrayValue[Index], Property, (char*)OutValue + Index * Property->GetElementSize(), ContainerStruct, Container, CheckFlags, SkipFlags))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ConvertScalarJsonValueToFPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
|
|
{
|
|
|
|
if (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
|
|
{
|
|
if (JsonValue->Type == EJson::String)
|
|
{
|
|
// see if we were passed a string for the enum
|
|
const UEnum* Enum = EnumProperty->GetEnum();
|
|
check(Enum);
|
|
FString StrValue = JsonValue->AsString();
|
|
int64 IntValue = Enum->GetValueByName(FName(*StrValue));
|
|
if (IntValue == INDEX_NONE)
|
|
{
|
|
UE_LOG(LogJson, Warning, TEXT("JsonValueToUProperty - Unable import enum %s from string value %s for property %s. Using default (first one)."), *Enum->CppType, *StrValue, *Property->GetNameCPP());
|
|
EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, (int64)0);
|
|
//return false;
|
|
}
|
|
else {
|
|
EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, IntValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// AsNumber will log an error for completely inappropriate types (then give us a default)
|
|
EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, (int64)JsonValue->AsNumber());
|
|
}
|
|
}
|
|
else if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Property))
|
|
{
|
|
if (NumericProperty->IsEnum() && JsonValue->Type == EJson::String)
|
|
{
|
|
// see if we were passed a string for the enum
|
|
const UEnum* Enum = NumericProperty->GetIntPropertyEnum();
|
|
check(Enum); // should be assured by IsEnum()
|
|
FString StrValue = JsonValue->AsString();
|
|
FName KeyStrValue = FName(*StrValue);
|
|
|
|
UUserDefinedEnum* UDEnum = Cast<UUserDefinedEnum>(NumericProperty->GetIntPropertyEnum());
|
|
if (UDEnum) {
|
|
for (const TTuple<FName, FText>& Tuple : UDEnum->DisplayNameMap) {
|
|
//if (Tuple.Value.EqualTo(FText::FromString(StrValue))) {
|
|
if (Tuple.Value.ToUpper().EqualTo(FText::FromString(StrValue).ToUpper())){
|
|
KeyStrValue = Tuple.Key;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//int64 IntValue = Enum->GetValueByName(FName(*StrValue));
|
|
int64 IntValue = Enum->GetValueByName(KeyStrValue);
|
|
if (IntValue == INDEX_NONE)
|
|
{
|
|
|
|
UE_LOG(LogJson, Warning, TEXT("JsonValueToUProperty - Unable import enum %s from string value %s for property %s. Using default (first one)."), *Enum->CppType, *StrValue, *Property->GetNameCPP());
|
|
NumericProperty->SetIntPropertyValue(OutValue, (int64)0);
|
|
//return false;
|
|
}
|
|
else {
|
|
NumericProperty->SetIntPropertyValue(OutValue, IntValue);
|
|
}
|
|
}
|
|
else if (NumericProperty->IsFloatingPoint())
|
|
{
|
|
// AsNumber will log an error for completely inappropriate types (then give us a default)
|
|
NumericProperty->SetFloatingPointPropertyValue(OutValue, JsonValue->AsNumber());
|
|
}
|
|
else if (NumericProperty->IsInteger())
|
|
{
|
|
if (JsonValue->Type == EJson::String)
|
|
{
|
|
// parse string -> int64 ourselves so we don't lose any precision going through AsNumber (aka double)
|
|
NumericProperty->SetIntPropertyValue(OutValue, FCString::Atoi64(*JsonValue->AsString()));
|
|
}
|
|
else
|
|
{
|
|
// AsNumber will log an error for completely inappropriate types (then give us a default)
|
|
NumericProperty->SetIntPropertyValue(OutValue, (int64)JsonValue->AsNumber());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to set numeric property type %s for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
|
|
{
|
|
// AsBool will log an error for completely inappropriate types (then give us a default)
|
|
BoolProperty->SetPropertyValue(OutValue, JsonValue->AsBool());
|
|
}
|
|
else if (FStrProperty* StringProperty = CastField<FStrProperty>(Property))
|
|
{
|
|
// AsString will log an error for completely inappropriate types (then give us a default)
|
|
StringProperty->SetPropertyValue(OutValue, JsonValue->AsString());
|
|
}
|
|
else if (FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
|
|
{
|
|
if (JsonValue->Type == EJson::Array)
|
|
{
|
|
TArray< TSharedPtr<FJsonValue> > ArrayValue = JsonValue->AsArray();
|
|
int32 ArrLen = ArrayValue.Num();
|
|
|
|
// make the output array size match
|
|
FScriptArrayHelper Helper(ArrayProperty, OutValue);
|
|
Helper.Resize(ArrLen);
|
|
|
|
// set the property values
|
|
for (int32 i = 0; i < ArrLen; ++i)
|
|
{
|
|
const TSharedPtr<FJsonValue>& ArrayValueItem = ArrayValue[i];
|
|
if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
|
|
{
|
|
if (!JsonValueToFPropertyWithContainer(ArrayValueItem, ArrayProperty->Inner, Helper.GetRawPtr(i), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to deserialize array element [%d] for property %s"), i, *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import TArray from non-array JSON key for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (FMapProperty* MapProperty = CastField<FMapProperty>(Property))
|
|
{
|
|
if (JsonValue->Type == EJson::Object)
|
|
{
|
|
TSharedPtr<FJsonObject> ObjectValue = JsonValue->AsObject();
|
|
|
|
FScriptMapHelper Helper(MapProperty, OutValue);
|
|
|
|
check(ObjectValue);
|
|
|
|
int32 MapSize = ObjectValue->Values.Num();
|
|
Helper.EmptyValues(MapSize);
|
|
|
|
// set the property values
|
|
for (const auto& Entry : ObjectValue->Values)
|
|
{
|
|
if (Entry.Value.IsValid() && !Entry.Value->IsNull())
|
|
{
|
|
int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
|
|
|
|
TSharedPtr<FJsonValueString> TempKeyValue = MakeShared<FJsonValueString>(Entry.Key);
|
|
|
|
const bool bKeySuccess = JsonValueToFPropertyWithContainer(TempKeyValue, MapProperty->KeyProp, Helper.GetKeyPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
|
|
const bool bValueSuccess = JsonValueToFPropertyWithContainer(Entry.Value, MapProperty->ValueProp, Helper.GetValuePtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
|
|
|
|
if (!(bKeySuccess && bValueSuccess))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to deserialize map element [key: %s] for property %s"), *Entry.Key, *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
Helper.Rehash();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import TMap from non-object JSON key for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (FSetProperty* SetProperty = CastField<FSetProperty>(Property))
|
|
{
|
|
if (JsonValue->Type == EJson::Array)
|
|
{
|
|
TArray< TSharedPtr<FJsonValue> > ArrayValue = JsonValue->AsArray();
|
|
int32 ArrLen = ArrayValue.Num();
|
|
|
|
FScriptSetHelper Helper(SetProperty, OutValue);
|
|
|
|
// set the property values
|
|
for (int32 i = 0; i < ArrLen; ++i)
|
|
{
|
|
const TSharedPtr<FJsonValue>& ArrayValueItem = ArrayValue[i];
|
|
if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
|
|
{
|
|
int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
|
|
if (!JsonValueToFPropertyWithContainer(ArrayValueItem, SetProperty->ElementProp, Helper.GetElementPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to deserialize set element [%d] for property %s"), i, *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
Helper.Rehash();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import TSet from non-array JSON key for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (FTextProperty* TextProperty = CastField<FTextProperty>(Property))
|
|
{
|
|
if (JsonValue->Type == EJson::String)
|
|
{
|
|
// assume this string is already localized, so import as invariant
|
|
TextProperty->SetPropertyValue(OutValue, FText::FromString(JsonValue->AsString()));
|
|
}
|
|
else if (JsonValue->Type == EJson::Object)
|
|
{
|
|
TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
|
|
check(Obj.IsValid()); // should not fail if Type == EJson::Object
|
|
|
|
// import the subvalue as a culture invariant string
|
|
FText Text;
|
|
if (!FJsonObjectConverter::GetTextFromObject(Obj.ToSharedRef(), Text))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import FText from JSON object with invalid keys for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
TextProperty->SetPropertyValue(OutValue, Text);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import FText from JSON that was neither string nor object for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (FStructProperty* StructProperty = CastField<FStructProperty>(Property))
|
|
{
|
|
static const FName NAME_DateTime1(TEXT("DateTime"));
|
|
static const FName NAME_Color1(TEXT("Color"));
|
|
static const FName NAME_LinearColor1(TEXT("LinearColor"));
|
|
if (JsonValue->Type == EJson::Object)
|
|
{
|
|
TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
|
|
check(Obj.IsValid()); // should not fail if Type == EJson::Object
|
|
if (!JsonAttributesToUStructWithContainer(Obj->Values, StructProperty->Struct, OutValue, ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_LinearColor1)
|
|
{
|
|
FLinearColor& ColorOut = *(FLinearColor*)OutValue;
|
|
FString ColorString = JsonValue->AsString();
|
|
|
|
FColor IntermediateColor;
|
|
IntermediateColor = FColor::FromHex(ColorString);
|
|
|
|
ColorOut = IntermediateColor;
|
|
}
|
|
else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_Color1)
|
|
{
|
|
FColor& ColorOut = *(FColor*)OutValue;
|
|
FString ColorString = JsonValue->AsString();
|
|
|
|
ColorOut = FColor::FromHex(ColorString);
|
|
}
|
|
else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_DateTime1)
|
|
{
|
|
FString DateString = JsonValue->AsString();
|
|
FDateTime& DateTimeOut = *(FDateTime*)OutValue;
|
|
if (DateString == TEXT("min"))
|
|
{
|
|
// min representable value for our date struct. Actual date may vary by platform (this is used for sorting)
|
|
DateTimeOut = FDateTime::MinValue();
|
|
}
|
|
else if (DateString == TEXT("max"))
|
|
{
|
|
// max representable value for our date struct. Actual date may vary by platform (this is used for sorting)
|
|
DateTimeOut = FDateTime::MaxValue();
|
|
}
|
|
else if (DateString == TEXT("now"))
|
|
{
|
|
// this value's not really meaningful from json serialization (since we don't know timezone) but handle it anyway since we're handling the other keywords
|
|
DateTimeOut = FDateTime::UtcNow();
|
|
}
|
|
else if (FDateTime::ParseIso8601(*DateString, DateTimeOut))
|
|
{
|
|
// ok
|
|
}
|
|
else if (FDateTime::Parse(DateString, DateTimeOut))
|
|
{
|
|
// ok
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to import FDateTime for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetCppStructOps() && StructProperty->Struct->GetCppStructOps()->HasImportTextItem())
|
|
{
|
|
UScriptStruct::ICppStructOps* TheCppStructOps = StructProperty->Struct->GetCppStructOps();
|
|
|
|
FString ImportTextString = JsonValue->AsString();
|
|
const TCHAR* ImportTextPtr = *ImportTextString;
|
|
if (!TheCppStructOps->ImportTextItem(ImportTextPtr, OutValue, PPF_None, nullptr, (FOutputDevice*)GWarn))
|
|
{
|
|
// Fall back to trying the tagged property approach if custom ImportTextItem couldn't get it done
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
if (Property->ImportText_Direct(ImportTextPtr, OutValue, nullptr, PPF_None) == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to import JSON string into %s property %s"), *StructProperty->Struct->GetAuthoredName(), *Property->GetAuthoredName());
|
|
return false;
|
|
}
|
|
#else
|
|
Property->ImportText(ImportTextPtr, OutValue, PPF_None, nullptr);
|
|
#endif
|
|
}
|
|
}
|
|
else if (JsonValue->Type == EJson::String)
|
|
{
|
|
FString ImportTextString = JsonValue->AsString();
|
|
const TCHAR* ImportTextPtr = *ImportTextString;
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
if (Property->ImportText_Direct(ImportTextPtr, OutValue, nullptr, PPF_None) == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to import JSON string into %s property %s"), *StructProperty->Struct->GetAuthoredName(), *Property->GetAuthoredName());
|
|
return false;
|
|
}
|
|
#else
|
|
Property->ImportText(ImportTextPtr, OutValue, PPF_None, nullptr);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Attempted to import UStruct from non-object JSON key for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))
|
|
{
|
|
if (JsonValue->Type == EJson::Object)
|
|
{
|
|
UObject* Outer = (UObject*)(GetTransientPackage());
|
|
if (ContainerStruct->IsChildOf(UObject::StaticClass()))
|
|
{
|
|
Outer = (UObject*)Container;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
|
|
UClass* PropertyClass = ObjectProperty->PropertyClass;
|
|
|
|
// If a specific subclass was stored in the Json, use that instead of the PropertyClass
|
|
FString ClassString = Obj->GetStringField(ObjectClassNameKey);
|
|
Obj->RemoveField(ObjectClassNameKey);
|
|
if (!ClassString.IsEmpty())
|
|
{
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
UClass* FoundClass = FPackageName::IsShortPackageName(ClassString) ? FindFirstObject<UClass>(*ClassString) : UClass::TryFindTypeSlow<UClass>(ClassString);
|
|
if (FoundClass)
|
|
{
|
|
PropertyClass = FoundClass;
|
|
}
|
|
#else
|
|
UClass* FoundClass = FindObject<UClass>((UObject*)(ANY_PACKAGE), *ClassString);
|
|
if (FoundClass)
|
|
{
|
|
PropertyClass = FoundClass;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
UObject* createdObj = StaticAllocateObject(PropertyClass, Outer, NAME_None, EObjectFlags::RF_NoFlags, EInternalObjectFlags::None, false);
|
|
|
|
#if ENGINE_MAJOR_VERSION > 4
|
|
(*PropertyClass->ClassConstructor)(FObjectInitializer(createdObj, PropertyClass->GetDefaultObject(), EObjectInitializerOptions::None));
|
|
#else
|
|
(*PropertyClass->ClassConstructor)(FObjectInitializer(createdObj, PropertyClass->GetDefaultObject(), false, false));
|
|
#endif
|
|
|
|
ObjectProperty->SetObjectPropertyValue(OutValue, createdObj);
|
|
|
|
check(Obj.IsValid()); // should not fail if Type == EJson::Object
|
|
if (!JsonAttributesToUStructWithContainer(Obj->Values, PropertyClass, createdObj, PropertyClass, createdObj, CheckFlags & (~CPF_ParmFlags), SkipFlags))
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
}
|
|
else if (JsonValue->Type == EJson::String)
|
|
{
|
|
// Default to expect a string for everything else
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
if (Property->ImportText_Direct(*JsonValue->AsString(), OutValue, nullptr, 0) == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to import JSON string into %s property %s"), *ObjectProperty->PropertyClass->GetAuthoredName(), *Property->GetAuthoredName());
|
|
return false;
|
|
}
|
|
#else
|
|
if (Property->ImportText(*JsonValue->AsString(), OutValue, 0, NULL) == NULL)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable import property type %s from string value for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
// Default to expect a string for everything else
|
|
if (Property->ImportText_Direct(*JsonValue->AsString(), OutValue, nullptr, 0) == nullptr)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable to import JSON string into property %s"), *Property->GetAuthoredName());
|
|
return false;
|
|
}
|
|
#else
|
|
if (Property->ImportText(*JsonValue->AsString(), OutValue, 0, NULL) == NULL)
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable import property type %s from string value for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool UStructToJsonAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonValue> >& OutJsonAttributes, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, bool bOmitEmpty)
|
|
{
|
|
if (SkipFlags == 0)
|
|
{
|
|
// If we have no specified skip flags, skip deprecated, transient and skip serialization by default when writing
|
|
SkipFlags |= CPF_Deprecated | CPF_Transient;
|
|
}
|
|
|
|
if (StructDefinition == FJsonObjectWrapper::StaticStruct())
|
|
{
|
|
// Just copy it into the object
|
|
const FJsonObjectWrapper* ProxyObject = (const FJsonObjectWrapper*)Struct;
|
|
|
|
if (ProxyObject->JsonObject.IsValid())
|
|
{
|
|
OutJsonAttributes = ProxyObject->JsonObject->Values;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ����struct��ÿ����Ա,���json value
|
|
for (TFieldIterator<FProperty> It(StructDefinition); It; ++It)
|
|
{
|
|
FProperty* Property = *It;
|
|
|
|
// Check to see if we should ignore this property
|
|
if (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags))
|
|
{
|
|
continue;
|
|
}
|
|
if (Property->HasAnyPropertyFlags(SkipFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//FString VariableName = FJsonObjectConverter::StandardizeCase(GetShortName(Property));
|
|
FString VariableName = GetShortName(Property);
|
|
const void* Value = Property->ContainerPtrToValuePtr<uint8>(Struct);
|
|
|
|
// convert the property to a FJsonValue
|
|
TSharedPtr<FJsonValue> JsonValue = UPropertyToJsonValue(Property, Value, CheckFlags, SkipFlags, ExportCb, nullptr, bOmitEmpty);
|
|
if (!JsonValue.IsValid())
|
|
{
|
|
FFieldClass* PropClass = Property->GetClass();
|
|
UE_LOG(LogJson, Error, TEXT("UPropertyToJsonValue - Unhandled property type '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* None,
|
|
Null,
|
|
String,
|
|
Number,
|
|
Boolean,
|
|
Array,
|
|
Object
|
|
*/
|
|
if (bOmitEmpty) {
|
|
switch (JsonValue->Type) {
|
|
case EJson::None:
|
|
{
|
|
continue;
|
|
}
|
|
case EJson::Null:
|
|
{
|
|
continue;
|
|
}
|
|
case EJson::String:
|
|
{
|
|
if (JsonValue->AsString().IsEmpty()) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case EJson::Number:
|
|
{
|
|
if (JsonValue->AsNumber() == 0) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case EJson::Array:
|
|
{
|
|
if (JsonValue->AsArray().Num() == 0) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case EJson::Boolean:
|
|
{
|
|
if (JsonValue->AsBool() == false) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case EJson::Object:
|
|
{
|
|
if (JsonValue->AsObject().IsValid() == false || JsonValue->AsObject()->Values.Num() == 0) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// set the value on the output object
|
|
OutJsonAttributes.Add(VariableName, JsonValue);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> UPropertyToJsonValue(FProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, FProperty* OuterProperty, bool bOmitEmpty)
|
|
{
|
|
if (Property->ArrayDim == 1)
|
|
{
|
|
return ConvertScalarFPropertyToJsonValue(Property, Value, CheckFlags, SkipFlags, ExportCb, OuterProperty, bOmitEmpty);
|
|
}
|
|
|
|
TArray< TSharedPtr<FJsonValue> > Array;
|
|
for (int Index = 0; Index != Property->ArrayDim; ++Index)
|
|
{
|
|
Array.Add(ConvertScalarFPropertyToJsonValue(Property, (char*)Value + Index * Property->GetElementSize(), CheckFlags, SkipFlags, ExportCb, OuterProperty, bOmitEmpty));
|
|
}
|
|
return MakeShared<FJsonValueArray>(Array);
|
|
}
|
|
|
|
|
|
TSharedPtr<FJsonValue> ConvertScalarFPropertyToJsonValue(FProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, FProperty* OuterProperty, bool bOmitEmpty)
|
|
{
|
|
// See if there's a custom export callback first, so it can override default behavior
|
|
if (ExportCb && ExportCb->IsBound())
|
|
{
|
|
TSharedPtr<FJsonValue> CustomValue = ExportCb->Execute(Property, Value);
|
|
if (CustomValue.IsValid())
|
|
{
|
|
return CustomValue;
|
|
}
|
|
// fall through to default cases
|
|
}
|
|
|
|
if (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
|
|
{
|
|
// export enums as strings
|
|
UEnum* EnumDef = EnumProperty->GetEnum();
|
|
FString StringValue = EnumDef->GetNameStringByValue(EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(Value));
|
|
return MakeShared<FJsonValueString>(StringValue);
|
|
}
|
|
else if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Property))
|
|
{
|
|
// see if it's an enum
|
|
UEnum* EnumDef = NumericProperty->GetIntPropertyEnum();
|
|
if (EnumDef != NULL)
|
|
{
|
|
FText StringValue = EnumDef->GetDisplayNameTextByIndex(NumericProperty->GetSignedIntPropertyValue(Value));
|
|
//EnumDef->GetDisplayNameTextByIndex
|
|
// export enums as strings
|
|
//FString StringValue = EnumDef->GetNameStringByValue(NumericProperty->GetSignedIntPropertyValue(Value));
|
|
return MakeShared<FJsonValueString>(StringValue.ToString());
|
|
}
|
|
|
|
// We want to export numbers as numbers
|
|
if (NumericProperty->IsFloatingPoint())
|
|
{
|
|
return MakeShared<FJsonValueNumber>(NumericProperty->GetFloatingPointPropertyValue(Value));
|
|
}
|
|
else if (NumericProperty->IsInteger())
|
|
{
|
|
return MakeShared<FJsonValueNumber>(NumericProperty->GetSignedIntPropertyValue(Value));
|
|
}
|
|
|
|
// fall through to default
|
|
}
|
|
else if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
|
|
{
|
|
// Export bools as bools
|
|
return MakeShared<FJsonValueBoolean>(BoolProperty->GetPropertyValue(Value));
|
|
}
|
|
else if (FStrProperty* StringProperty = CastField<FStrProperty>(Property))
|
|
{
|
|
return MakeShared<FJsonValueString>(StringProperty->GetPropertyValue(Value));
|
|
}
|
|
else if (FTextProperty* TextProperty = CastField<FTextProperty>(Property))
|
|
{
|
|
return MakeShared<FJsonValueString>(TextProperty->GetPropertyValue(Value).ToString());
|
|
}
|
|
else if (FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
|
|
{
|
|
TArray< TSharedPtr<FJsonValue> > Out;
|
|
FScriptArrayHelper Helper(ArrayProperty, Value);
|
|
for (int32 i = 0, n = Helper.Num(); i < n; ++i)
|
|
{
|
|
TSharedPtr<FJsonValue> Elem = UPropertyToJsonValue(ArrayProperty->Inner, Helper.GetRawPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, ArrayProperty, bOmitEmpty);
|
|
if (Elem.IsValid())
|
|
{
|
|
// add to the array
|
|
Out.Push(Elem);
|
|
}
|
|
}
|
|
return MakeShared<FJsonValueArray>(Out);
|
|
}
|
|
else if (FSetProperty* SetProperty = CastField<FSetProperty>(Property))
|
|
{
|
|
TArray< TSharedPtr<FJsonValue> > Out;
|
|
FScriptSetHelper Helper(SetProperty, Value);
|
|
for (int32 i = 0, n = Helper.Num(); n; ++i)
|
|
{
|
|
if (Helper.IsValidIndex(i))
|
|
{
|
|
TSharedPtr<FJsonValue> Elem = UPropertyToJsonValue(SetProperty->ElementProp, Helper.GetElementPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, SetProperty, bOmitEmpty);
|
|
if (Elem.IsValid())
|
|
{
|
|
// add to the array
|
|
Out.Push(Elem);
|
|
}
|
|
|
|
--n;
|
|
}
|
|
}
|
|
return MakeShared<FJsonValueArray>(Out);
|
|
}
|
|
else if (FMapProperty* MapProperty = CastField<FMapProperty>(Property))
|
|
{
|
|
TSharedRef<FJsonObject> Out = MakeShared<FJsonObject>();
|
|
|
|
FScriptMapHelper Helper(MapProperty, Value);
|
|
for (int32 i = 0, n = Helper.Num(); n; ++i)
|
|
{
|
|
if (Helper.IsValidIndex(i))
|
|
{
|
|
TSharedPtr<FJsonValue> KeyElement = UPropertyToJsonValue(MapProperty->KeyProp, Helper.GetKeyPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, MapProperty, bOmitEmpty);
|
|
TSharedPtr<FJsonValue> ValueElement = UPropertyToJsonValue(MapProperty->ValueProp, Helper.GetValuePtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, MapProperty, bOmitEmpty);
|
|
if (KeyElement.IsValid() && ValueElement.IsValid())
|
|
{
|
|
FString KeyString;
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
if (!KeyElement->TryGetString(KeyString))
|
|
{
|
|
MapProperty->KeyProp->ExportTextItem_Direct(KeyString, Helper.GetKeyPtr(i), nullptr, nullptr, 0);
|
|
if (KeyString.IsEmpty())
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("Unable to convert key to string for property %s."), *MapProperty->GetAuthoredName())
|
|
KeyString = FString::Printf(TEXT("Unparsed Key %d"), i);
|
|
}
|
|
}
|
|
|
|
// Coerce camelCase map keys for Enum/FName properties
|
|
if (CastField<FEnumProperty>(MapProperty->KeyProp) ||
|
|
CastField<FNameProperty>(MapProperty->KeyProp))
|
|
{
|
|
KeyString = FJsonObjectConverter::StandardizeCase(KeyString);
|
|
}
|
|
#else
|
|
if (!KeyElement->TryGetString(KeyString))
|
|
{
|
|
MapProperty->KeyProp->ExportTextItem(KeyString, Helper.GetKeyPtr(i), nullptr, nullptr, 0);
|
|
if (KeyString.IsEmpty())
|
|
{
|
|
UE_LOG(LogJson, Error, TEXT("Unable to convert key to string for property %s."), *MapProperty->GetName())
|
|
KeyString = FString::Printf(TEXT("Unparsed Key %d"), i);
|
|
}
|
|
}
|
|
#endif
|
|
Out->SetField(KeyString, ValueElement);
|
|
}
|
|
|
|
--n;
|
|
}
|
|
}
|
|
|
|
return MakeShared<FJsonValueObject>(Out);
|
|
}
|
|
else if (FStructProperty* StructProperty = CastField<FStructProperty>(Property))
|
|
{
|
|
UScriptStruct::ICppStructOps* TheCppStructOps = StructProperty->Struct->GetCppStructOps();
|
|
// Intentionally exclude the JSON Object wrapper, which specifically needs to export JSON in an object representation instead of a string
|
|
if (StructProperty->Struct != FJsonObjectWrapper::StaticStruct() && TheCppStructOps && TheCppStructOps->HasExportTextItem())
|
|
{
|
|
FString OutValueStr;
|
|
TheCppStructOps->ExportTextItem(OutValueStr, Value, nullptr, nullptr, PPF_None, nullptr);
|
|
return MakeShared<FJsonValueString>(OutValueStr);
|
|
}
|
|
|
|
TSharedRef<FJsonObject> Out = MakeShared<FJsonObject>();
|
|
if (UStructToJsonObject(StructProperty->Struct, Value, Out, CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, bOmitEmpty))
|
|
{
|
|
return MakeShared<FJsonValueObject>(Out);
|
|
}
|
|
}
|
|
else if (FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))
|
|
{
|
|
// Instanced properties should be copied by value, while normal UObject* properties should output as asset references
|
|
UObject* Object = ObjectProperty->GetObjectPropertyValue(Value);
|
|
if (Object && (ObjectProperty->HasAnyPropertyFlags(CPF_PersistentInstance) || (OuterProperty && OuterProperty->HasAnyPropertyFlags(CPF_PersistentInstance))))
|
|
{
|
|
TSharedRef<FJsonObject> Out = MakeShared<FJsonObject>();
|
|
|
|
Out->SetStringField(ObjectClassNameKey, Object->GetClass()->GetFName().ToString());
|
|
if (UStructToJsonObject(ObjectProperty->GetObjectPropertyValue(Value)->GetClass(), Object, Out, CheckFlags, SkipFlags, ExportCb, bOmitEmpty))
|
|
{
|
|
TSharedRef<FJsonValueObject> JsonObject = MakeShared<FJsonValueObject>(Out);
|
|
JsonObject->Type = EJson::Object;
|
|
return JsonObject;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FString StringValue;
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
Property->ExportTextItem_Direct(StringValue, Value, nullptr, nullptr, PPF_None);
|
|
#else
|
|
Property->ExportTextItem(StringValue, Value, nullptr, nullptr, PPF_None);
|
|
#endif
|
|
return MakeShared<FJsonValueString>(StringValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Default to export as string for everything else
|
|
FString StringValue;
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
|
Property->ExportTextItem_Direct(StringValue, Value, NULL, NULL, PPF_None);
|
|
#else
|
|
Property->ExportTextItem(StringValue, Value, NULL, NULL, PPF_None);
|
|
#endif
|
|
return MakeShared<FJsonValueString>(StringValue);
|
|
}
|
|
|
|
// invalid
|
|
return TSharedPtr<FJsonValue>();
|
|
}
|
|
|
|
bool UStructToJsonObject(const UStruct* StructDefinition, const void* Struct, TSharedRef<FJsonObject> OutJsonObject, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, bool bOmitEmpty) {
|
|
return UStructToJsonAttributes(StructDefinition, Struct, OutJsonObject->Values, CheckFlags, SkipFlags, ExportCb, bOmitEmpty);
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|