diff --git a/Unreal/Content/Project/BP/Avatars/PersonalityConfigs/DA_PersonalityConfig_Ben_BREX.uasset b/Unreal/Content/Project/BP/Avatars/PersonalityConfigs/DA_PersonalityConfig_Ben_BREX.uasset index eb3110b..2ef311c 100644 Binary files a/Unreal/Content/Project/BP/Avatars/PersonalityConfigs/DA_PersonalityConfig_Ben_BREX.uasset and b/Unreal/Content/Project/BP/Avatars/PersonalityConfigs/DA_PersonalityConfig_Ben_BREX.uasset differ diff --git a/Unreal/Content/Project/BP/BP_Project_Manager.uasset b/Unreal/Content/Project/BP/BP_Project_Manager.uasset index 3541dd7..135e434 100644 Binary files a/Unreal/Content/Project/BP/BP_Project_Manager.uasset and b/Unreal/Content/Project/BP/BP_Project_Manager.uasset differ diff --git a/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset b/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset index 429744e..e3b2255 100644 Binary files a/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset and b/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset differ diff --git a/Unreal/Content/Project/BP/Prompts/DA_Prompts_Base.uasset b/Unreal/Content/Project/BP/Prompts/DA_Prompts_Base.uasset index 6be951b..37608f9 100644 Binary files a/Unreal/Content/Project/BP/Prompts/DA_Prompts_Base.uasset and b/Unreal/Content/Project/BP/Prompts/DA_Prompts_Base.uasset differ diff --git a/Unreal/Content/Project/BP/Prompts/Data/PDA_Prompts.uasset b/Unreal/Content/Project/BP/Prompts/Data/PDA_Prompts.uasset index fb97f2b..5711328 100644 Binary files a/Unreal/Content/Project/BP/Prompts/Data/PDA_Prompts.uasset and b/Unreal/Content/Project/BP/Prompts/Data/PDA_Prompts.uasset differ diff --git a/Unreal/Content/Project/BP/Prompts/Data/S_Prompts.uasset b/Unreal/Content/Project/BP/Prompts/Data/S_Prompts.uasset index 28491ef..b1e2187 100644 Binary files a/Unreal/Content/Project/BP/Prompts/Data/S_Prompts.uasset and b/Unreal/Content/Project/BP/Prompts/Data/S_Prompts.uasset differ diff --git a/Unreal/Content/Schema/Spie_Config.schema.json b/Unreal/Content/Schema/Spie_Config.schema.json index e75cef1..098b4d5 100644 --- a/Unreal/Content/Schema/Spie_Config.schema.json +++ b/Unreal/Content/Schema/Spie_Config.schema.json @@ -277,6 +277,18 @@ "category": "STT Settings" } }, + { + "AzureSpeechService_PhraseList": + { + "type": "array", + "default": [ + "SPIE" + ], + "tooltip": "Additional Phrases to give to Azure to better transcribe the audio", + "category": "STT Settings", + "itemsType": "string" + } + }, { "STTSettings": { @@ -321,7 +333,16 @@ "SpeexDSP_AEC_Tail": 500, "bSpeexDSP_UseAEC": false }, - "STTReplacements": [] + "STTReplacements": [ + { + "TranscribedWords": [ + "spie", + "schpie", + "spieh" + ], + "ReplacementWord": "SPIE" + } + ] }, "category": "STT Settings", "fields": diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset index e593863..7f98be8 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/PDA_PersonalityConfig.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/PDA_PersonalityConfig.uasset index 65efd6c..dcd3285 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/PDA_PersonalityConfig.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/PDA_PersonalityConfig.uasset differ diff --git a/Unreal/Plugins/StructToJSONPlugin/EasyStruct2Json.uplugin b/Unreal/Plugins/StructToJSONPlugin/EasyStruct2Json.uplugin new file mode 100644 index 0000000..886c7f6 --- /dev/null +++ b/Unreal/Plugins/StructToJSONPlugin/EasyStruct2Json.uplugin @@ -0,0 +1,28 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "Struct2Json", + "Description": "Convert User Defined Struct to and from Json", + "Category": "Other", + "CreatedBy": "dan", + "CreatedByURL": "https://github.com/DAN-AND-DNA/", + "DocsURL": "https://docs.google.com/document/d/1Bqf_kjC484IPwsgcWBnXuXMCRY5AJPzvM0mCJBUGJxc/edit?usp=sharing", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/ba62fdd8e61a4053ac7a3e6497f25d6c", + "SupportURL": "https://github.com/DAN-AND-DNA/UE_EasyStruct2Json", + "CanContainContent": true, + "Installed": true, + "Modules": [ + { + "Name": "EasyStruct2Json", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64", + "Mac", + "IOS", + "Android" + ] + } + ] +} \ No newline at end of file diff --git a/Unreal/Plugins/StructToJSONPlugin/Resources/Icon128.png b/Unreal/Plugins/StructToJSONPlugin/Resources/Icon128.png new file mode 100644 index 0000000..9ac720f Binary files /dev/null and b/Unreal/Plugins/StructToJSONPlugin/Resources/Icon128.png differ diff --git a/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/EasyStruct2Json.Build.cs b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/EasyStruct2Json.Build.cs new file mode 100644 index 0000000..e0fd077 --- /dev/null +++ b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/EasyStruct2Json.Build.cs @@ -0,0 +1,58 @@ +// Copyright 2022 Danyang Chen https://github.com/DAN-AND-DNA + +using UnrealBuildTool; + +public class EasyStruct2Json : ModuleRules +{ + public EasyStruct2Json(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + //OptimizeCode = CodeOptimization.Never; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "Json", + "JsonUtilities", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + + //OptimizeCode = CodeOptimization.Never; + } +} diff --git a/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Private/EasyStruct2Json.cpp b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Private/EasyStruct2Json.cpp new file mode 100644 index 0000000..6fca632 --- /dev/null +++ b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Private/EasyStruct2Json.cpp @@ -0,0 +1,20 @@ +// Copyright 2022 Danyang Chen https://github.com/DAN-AND-DNA + +#include "EasyStruct2Json.h" + +#define LOCTEXT_NAMESPACE "FEasyStruct2JsonModule" + +void FEasyStruct2JsonModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FEasyStruct2JsonModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FEasyStruct2JsonModule, EasyStruct2Json) \ No newline at end of file diff --git a/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Private/Struct2JsonLibrary.cpp b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Private/Struct2JsonLibrary.cpp new file mode 100644 index 0000000..aa26118 --- /dev/null +++ b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Private/Struct2JsonLibrary.cpp @@ -0,0 +1,980 @@ +// 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 >& OutJsonAttributes, int64 CheckFlags = 0, int64 SkipFlags = 0, const FJsonObjectConverter::CustomExportCallback* ExportCb = nullptr, bool bOmitEmpty = false); + bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags); + bool JsonValueToFPropertyWithContainer(const TSharedPtr& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags); + bool ConvertScalarJsonValueToFPropertyWithContainer(const TSharedPtr& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags); + TSharedPtr UPropertyToJsonValue(FProperty* Property, const void* Value, int64 CheckFlags = 0, int64 SkipFlags = 0, const FJsonObjectConverter::CustomExportCallback* ExportCb = nullptr, FProperty* OuterProperty = nullptr, bool bOmitEmpty = false); + TSharedPtr 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 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 JsonObject; + TSharedRef > 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 +bool UStructToJsonObjectStringInternal(const TSharedRef& JsonObject, FString& OutJsonString, int32 Indent) +{ + TSharedRef > JsonWriter = TJsonWriterFactory::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 JsonObject = MakeShared(); + // ��struct���json value + if (::UStructToJsonAttributes(Struct, StructPtr, JsonObject->Values, 1, 0, nullptr, bOmitEmpty)) { + bool bSuccess = false; + if(bPretty) { + bSuccess = UStructToJsonObjectStringInternal >(JsonObject, OutJsonStr, 0); + } else { + bSuccess = UStructToJsonObjectStringInternal >(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 >& 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(); + ProxyObject->JsonObject->Values = JsonAttributes; + return true; + } + + int32 NumUnclaimedProperties = JsonAttributes.Num(); + if (NumUnclaimedProperties <= 0) + { + return true; + } + + // iterate over the struct properties + for (TFieldIterator 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* JsonValue = JsonAttributes.Find(Property->GetName()); + const TSharedPtr* 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(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& 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() || Property->IsA(); + 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& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags) + { + + if (FEnumProperty* EnumProperty = CastField(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(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(NumericProperty->GetIntPropertyEnum()); + if (UDEnum) { + for (const TTuple& 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(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(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(Property)) + { + if (JsonValue->Type == EJson::Array) + { + TArray< TSharedPtr > 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& 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(Property)) + { + if (JsonValue->Type == EJson::Object) + { + TSharedPtr 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 TempKeyValue = MakeShared(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(Property)) + { + if (JsonValue->Type == EJson::Array) + { + TArray< TSharedPtr > ArrayValue = JsonValue->AsArray(); + int32 ArrLen = ArrayValue.Num(); + + FScriptSetHelper Helper(SetProperty, OutValue); + + // set the property values + for (int32 i = 0; i < ArrLen; ++i) + { + const TSharedPtr& 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(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 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(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 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(Property)) + { + if (JsonValue->Type == EJson::Object) + { + UObject* Outer = (UObject*)(GetTransientPackage()); + if (ContainerStruct->IsChildOf(UObject::StaticClass())) + { + Outer = (UObject*)Container; + } + + TSharedPtr 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(*ClassString) : UClass::TryFindTypeSlow(ClassString); + if (FoundClass) + { + PropertyClass = FoundClass; + } +#else + UClass* FoundClass = FindObject((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 >& 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 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(Struct); + + // convert the property to a FJsonValue + TSharedPtr 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 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 > 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(Array); + } + + + TSharedPtr 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 CustomValue = ExportCb->Execute(Property, Value); + if (CustomValue.IsValid()) + { + return CustomValue; + } + // fall through to default cases + } + + if (FEnumProperty* EnumProperty = CastField(Property)) + { + // export enums as strings + UEnum* EnumDef = EnumProperty->GetEnum(); + FString StringValue = EnumDef->GetNameStringByValue(EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(Value)); + return MakeShared(StringValue); + } + else if (FNumericProperty* NumericProperty = CastField(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(StringValue.ToString()); + } + + // We want to export numbers as numbers + if (NumericProperty->IsFloatingPoint()) + { + return MakeShared(NumericProperty->GetFloatingPointPropertyValue(Value)); + } + else if (NumericProperty->IsInteger()) + { + return MakeShared(NumericProperty->GetSignedIntPropertyValue(Value)); + } + + // fall through to default + } + else if (FBoolProperty* BoolProperty = CastField(Property)) + { + // Export bools as bools + return MakeShared(BoolProperty->GetPropertyValue(Value)); + } + else if (FStrProperty* StringProperty = CastField(Property)) + { + return MakeShared(StringProperty->GetPropertyValue(Value)); + } + else if (FTextProperty* TextProperty = CastField(Property)) + { + return MakeShared(TextProperty->GetPropertyValue(Value).ToString()); + } + else if (FArrayProperty* ArrayProperty = CastField(Property)) + { + TArray< TSharedPtr > Out; + FScriptArrayHelper Helper(ArrayProperty, Value); + for (int32 i = 0, n = Helper.Num(); i < n; ++i) + { + TSharedPtr 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(Out); + } + else if (FSetProperty* SetProperty = CastField(Property)) + { + TArray< TSharedPtr > Out; + FScriptSetHelper Helper(SetProperty, Value); + for (int32 i = 0, n = Helper.Num(); n; ++i) + { + if (Helper.IsValidIndex(i)) + { + TSharedPtr 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(Out); + } + else if (FMapProperty* MapProperty = CastField(Property)) + { + TSharedRef Out = MakeShared(); + + FScriptMapHelper Helper(MapProperty, Value); + for (int32 i = 0, n = Helper.Num(); n; ++i) + { + if (Helper.IsValidIndex(i)) + { + TSharedPtr KeyElement = UPropertyToJsonValue(MapProperty->KeyProp, Helper.GetKeyPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, MapProperty, bOmitEmpty); + TSharedPtr 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(MapProperty->KeyProp) || + CastField(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(Out); + } + else if (FStructProperty* StructProperty = CastField(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(OutValueStr); + } + + TSharedRef Out = MakeShared(); + if (UStructToJsonObject(StructProperty->Struct, Value, Out, CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb, bOmitEmpty)) + { + return MakeShared(Out); + } + } + else if (FObjectProperty* ObjectProperty = CastField(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 Out = MakeShared(); + + Out->SetStringField(ObjectClassNameKey, Object->GetClass()->GetFName().ToString()); + if (UStructToJsonObject(ObjectProperty->GetObjectPropertyValue(Value)->GetClass(), Object, Out, CheckFlags, SkipFlags, ExportCb, bOmitEmpty)) + { + TSharedRef JsonObject = MakeShared(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(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(StringValue); + } + + // invalid + return TSharedPtr(); + } + + bool UStructToJsonObject(const UStruct* StructDefinition, const void* Struct, TSharedRef OutJsonObject, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverter::CustomExportCallback* ExportCb, bool bOmitEmpty) { + return UStructToJsonAttributes(StructDefinition, Struct, OutJsonObject->Values, CheckFlags, SkipFlags, ExportCb, bOmitEmpty); + } + +} + +#endif \ No newline at end of file diff --git a/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Public/EasyStruct2Json.h b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Public/EasyStruct2Json.h new file mode 100644 index 0000000..f312bed --- /dev/null +++ b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Public/EasyStruct2Json.h @@ -0,0 +1,15 @@ +// Copyright 2022 Danyang Chen https://github.com/DAN-AND-DNA + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FEasyStruct2JsonModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Public/Struct2JsonLibrary.h b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Public/Struct2JsonLibrary.h new file mode 100644 index 0000000..6237360 --- /dev/null +++ b/Unreal/Plugins/StructToJSONPlugin/Source/EasyStruct2Json/Public/Struct2JsonLibrary.h @@ -0,0 +1,68 @@ +// Copyright 2022 Danyang Chen https://github.com/DAN-AND-DNA + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Runtime/Launch/Resources/Version.h" +#include "Struct2JsonLibrary.generated.h" + +UCLASS() +class UWrapperStructProperty : public UObject { + GENERATED_BODY() +public: + FStructProperty* InterStruct; +}; + +UCLASS() +class EASYSTRUCT2JSON_API UStruct2JsonLibaray : public UBlueprintFunctionLibrary { + GENERATED_BODY() +public: + + UFUNCTION(BlueprintCallable, Category = "Struct2Json", CustomThunk, meta = (CustomStructureParam = "OutStruct")) + static UPARAM(DisplayName = "Ok") bool Json2Struct(const FString& JsonStr, const int32& OutStruct); + + DECLARE_FUNCTION(execJson2Struct) { +#if (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 26 || ENGINE_MAJOR_VERSION > 4) + + P_GET_PROPERTY(FStrProperty, JsonStr); + + Stack.StepCompiledIn(NULL); + FStructProperty* StructProperty = ExactCastField(Stack.MostRecentProperty); + void* StructPropertyPtr = Stack.MostRecentPropertyAddress; + P_FINISH; + + *(bool*) RESULT_PARAM = JsonStr2Struct(StructProperty, StructPropertyPtr, JsonStr); +#else + *(bool*) RESULT_PARAM = false; +#endif + } + + + + UFUNCTION(BlueprintCallable, Category = "Struct2Json", CustomThunk, meta = (CustomStructureParam = "InStruct")) + static UPARAM(DisplayName = "Ok") bool Struct2Json(const int32& InStruct, FString& JsonStr, bool bPretty = false, bool bOmitEmpty = true); + + DECLARE_FUNCTION(execStruct2Json) { +#if (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 26 || ENGINE_MAJOR_VERSION > 4) + + Stack.StepCompiledIn(NULL); + FStructProperty* StructProperty = ExactCastField(Stack.MostRecentProperty); + void* StructPropertyPtr = Stack.MostRecentPropertyAddress; + + P_GET_PROPERTY_REF(FStrProperty, JsonStr); + P_GET_PROPERTY(FBoolProperty, bPretty); + P_GET_PROPERTY(FBoolProperty, bOmitEmpty); + P_FINISH; + + *(bool*)RESULT_PARAM = Struct2JsonStr(StructProperty, StructPropertyPtr, JsonStr, bPretty, bOmitEmpty); +#else + *(bool*)RESULT_PARAM = false; +#endif + } + + + static bool JsonStr2Struct(FStructProperty* StructProperty, void* StructPtr, const FString& InJsonStr); + static bool Struct2JsonStr(FStructProperty* StructProperty, void* StructPtr, FString& OutJsonStr, bool bPretty, bool bOmitEmpty); + +}; \ No newline at end of file