Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 1606|回复: 0

UE4笔记-UStructToJsonObjectString首字母自动转换为小写的问题及解决方法

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-15 11:04:12 | 显示全部楼层 |阅读模式

    UE4:

      JsonUtilities模块的FJsonObjectConverter::UStructToJsonObjectString 会自动将json key的首字母转换为小写

      例如我有个

    USTRUCT(BlueprintType)
    struct FDTOData_Test 
    {
        GENERATED_USTRUCT_BODY()
    public:
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FDTO_AssetItem")
            FString TiTle;
    };

    UStructToJsonObjectString 后会变成:

        FDTOData_Test data;
        data.Title = TEXT("sssss");
    FJsonObjectConverter::UStructToJsonObjectString(data , ret_json, 0, 0);
    /*
      {
        "tiTle" : "sssss"
      }
    */

     

    原因是JsonObjectConverter.cpp中的StandardizeCase这个智障函数..将所有的属性名的首字母都修改成了lowerCase.

    具体代码在JsonObjectConverter.cpp 261行

    如下:

    FString VariableName = StandardizeCase(Property->GetName());

     

     

    解决方法:

    知道原因就很好去处理问题了...

    UE4 源码版:

    修改JsonObjectConverter.cpp的261行(代码位于Runtime\JsonUtilities\Private\JsonObjectConverter.cpp)

    FString VariableName = StandardizeCase(Property->GetName());

    修改至:

    FString VariableName =Property->GetName();

     

    UE4 二进制安装版:

    build.cs添加一下模块

                "Core"                  ,
                "CoreUObject"           ,
                "Json"                  ,
                "JsonUtilities"         

     

    并使用以下修改扩展类进行UStructToJsonObjectString:

    FJsonObjectConverter扩展类FJsonObjectConverterEx代码:

    // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "UObject/Class.h"
    #include "Serialization/JsonTypes.h"
    #include "Dom/JsonObject.h"
    #include "Serialization/JsonReader.h"
    #include "Serialization/JsonSerializer.h"
    #include "JsonObjectWrapper.h"
    
    /** Class that handles converting Json objects to and from UStructs */
    class  FJsonObjectConverterEX
    {
    public:
    
        /** FName case insensitivity can make the casing of UPROPERTIES unpredictable. Attempt to standardize output. */
        static FString StandardizeCase(const FString &StringIn);
    
        /** Parse an FText from a json object (assumed to be of the form where keys are culture codes and values are strings) */
        static bool GetTextFromObject(const TSharedRef<FJsonObject>& Obj, FText& TextOut);
    
    public: // UStruct -> JSON
    
        /**
         * Optional callback to run when exporting a type which we don't already understand.
         * If this returns a valid pointer it will be inserted into the export chain. If not, or if this is not
         * passed in, then we will call the generic ToString on the type and export as a JSON string.
         */
        DECLARE_DELEGATE_RetVal_TwoParams(TSharedPtr<FJsonValue>, CustomExportCallback, UProperty* /* Property */, const void* /* Value */);
    
        /**
         * Utility Export Callback for having object properties expanded to full Json.
         */
        static TSharedPtr<FJsonValue> ObjectJsonCallback(UProperty* Property , const void* Value);
    
        /**
         * Templated version of UStructToJsonObject to try and make most of the params. Also serves as an example use case
         *
         * @param InStruct The UStruct instance to read from
         * @param ExportCb Optional callback for types we don't understand. This is called right before falling back to the generic ToString()
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         * @return FJsonObject pointer. Invalid if an error occurred.
         */
        template<typename InStructType>
        static TSharedPtr<FJsonObject> UStructToJsonObject(const InStructType& InStruct, int64 CheckFlags = 0, int64 SkipFlags = 0, const CustomExportCallback* ExportCb = nullptr)
        {
            TSharedRef<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
            if (UStructToJsonObject(InStructType::StaticStruct(), &InStruct, JsonObject, CheckFlags, SkipFlags, ExportCb))
            {
                return JsonObject;
            }
            return TSharedPtr<FJsonObject>(); // something went wrong
        }
    
        /**
         * Converts from a UStruct to a Json Object, using exportText
         *
         * @param StructDefinition UStruct definition that is looked over for properties
         * @param Struct The UStruct instance to copy out of
         * @param JsonObject Json Object to be filled in with data from the ustruct
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         * @param ExportCb Optional callback for types we don't understand. This is called right before falling back to the generic ToString()
         *
         * @return False if any properties failed to write
         */
        static bool UStructToJsonObject(const UStruct* StructDefinition, const void* Struct, TSharedRef<FJsonObject> OutJsonObject, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb = nullptr);
    
        /**
         * Converts from a UStruct to a json string containing an object, using exportText
         *
         * @param StructDefinition UStruct definition that is looked over for properties
         * @param Struct The UStruct instance to copy out of
         * @param JsonObject Json Object to be filled in with data from the ustruct
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         * @param Indent How many tabs to add to the json serializer
         * @param ExportCb Optional callback for types we don't understand. This is called right before falling back to the generic ToString()
         * @param bPrettyPrint Option to use pretty print (e.g., adds line endings) or condensed print
         *
         * @return False if any properties failed to write
         */
        static bool UStructToJsonObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonString, int64 CheckFlags, int64 SkipFlags, int32 Indent = 0, const CustomExportCallback* ExportCb = nullptr, bool bPrettyPrint = true);
    
        /**
         * Templated version; Converts from a UStruct to a json string containing an object, using exportText
         *
         * @param Struct The UStruct instance to copy out of
         * @param JsonObject Json Object to be filled in with data from the ustruct
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         * @param Indent How many tabs to add to the json serializer
         * @param ExportCb Optional callback for types we don't understand. This is called right before falling back to the generic ToString()
         * @param bPrettyPrint Option to use pretty print (e.g., adds line endings) or condensed print
         *
         * @return False if any properties failed to write
         */
        template<typename InStructType>
        static bool UStructToJsonObjectString(const InStructType& InStruct, FString& OutJsonString, int64 CheckFlags = 0, int64 SkipFlags = 0, int32 Indent = 0, const CustomExportCallback* ExportCb = nullptr, bool bPrettyPrint = true)
        {
            return UStructToJsonObjectString(InStructType::StaticStruct(), &InStruct, OutJsonString, CheckFlags, SkipFlags, Indent, ExportCb, bPrettyPrint);
        }
    
        /**
         * Wrapper to UStructToJsonObjectString that allows a print policy to be specified.
         */
        template<typename CharType, template<typename> class PrintPolicy>
        static bool UStructToFormattedJsonObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonString, int64 CheckFlags, int64 SkipFlags, int32 Indent = 0, const CustomExportCallback* ExportCb = nullptr)
        {
            TSharedRef<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
            if (UStructToJsonObject(StructDefinition, Struct, JsonObject, CheckFlags, SkipFlags, ExportCb))
            {
                TSharedRef<TJsonWriter<CharType, PrintPolicy<CharType>>> JsonWriter = TJsonWriterFactory<CharType, PrintPolicy<CharType>>::Create(&OutJsonString, Indent);
    
                if (FJsonSerializer::Serialize(JsonObject, JsonWriter))
                {
                    JsonWriter->Close();
                    return true;
                }
                else
                {
                    UE_LOG(LogJson, Warning, TEXT("UStructToFormattedObjectString - Unable to write out json"));
                    JsonWriter->Close();
                }
            }
    
            return false;
        }
    
        /**
         * Converts from a UStruct to a set of json attributes (possibly from within a JsonObject)
         *
         * @param StructDefinition UStruct definition that is looked over for properties
         * @param Struct The UStruct instance to copy out of
         * @param JsonAttributes Map of attributes to copy in to
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         * @param ExportCb Optional callback for types we don't understand. This is called right before falling back to the generic ToString()
         *
         * @return False if any properties failed to write
         */
        static bool UStructToJsonAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonValue> >& OutJsonAttributes, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb = nullptr);
    
        /* * Converts from a UProperty to a Json Value using exportText
         *
         * @param Property            The property to export
         * @param Value                Pointer to the value of the property
         * @param CheckFlags        Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags            Skip properties that match any of these flags
         * @param ExportCb            Optional callback for types we don't understand. This is called right before falling back to the generic ToString()
         *
         * @return                    The constructed JsonValue from the property
         */
        static TSharedPtr<FJsonValue> UPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb = nullptr);
    
    public: // JSON -> UStruct
    
        /**
         * Converts from a Json Object to a UStruct, using importText
         *
         * @param JsonObject Json Object to copy data out of
         * @param StructDefinition UStruct definition that is looked over for properties
         * @param Struct The UStruct instance to copy in to
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         *
         * @return False if any properties matched but failed to deserialize
         */
        static bool JsonObjectToUStruct(const TSharedRef<FJsonObject>& JsonObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags = 0, int64 SkipFlags = 0);
    
        /**
         * Templated version of JsonObjectToUStruct
         *
         * @param JsonObject Json Object to copy data out of
         * @param OutStruct The UStruct instance to copy in to
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         *
         * @return False if any properties matched but failed to deserialize
         */
        template<typename OutStructType>
        static bool JsonObjectToUStruct(const TSharedRef<FJsonObject>& JsonObject, OutStructType* OutStruct, int64 CheckFlags = 0, int64 SkipFlags = 0)
        {
            return JsonObjectToUStruct(JsonObject, OutStructType::StaticStruct(), OutStruct, CheckFlags, SkipFlags);
        }
    
        /**
         * Converts a set of json attributes (possibly from within a JsonObject) to a UStruct, using importText
         *
         * @param JsonAttributes Json Object to copy data out of
         * @param StructDefinition UStruct definition that is looked over for properties
         * @param OutStruct The UStruct instance to copy in to
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         *
         * @return False if any properties matched but failed to deserialize
         */
        static bool JsonAttributesToUStruct(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags);
    
        /**
         * Converts a single JsonValue to the corresponding UProperty (this may recurse if the property is a UStruct for instance).
         *
         * @param JsonValue The value to assign to this property
         * @param Property The UProperty definition of the property we're setting.
         * @param OutValue Pointer to the property instance to be modified.
         * @param CheckFlags Only convert sub-properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip sub-properties that match any of these flags
         *
         * @return False if the property failed to serialize
         */
        static bool JsonValueToUProperty(const TSharedPtr<FJsonValue>& JsonValue, UProperty* Property, void* OutValue, int64 CheckFlags, int64 SkipFlags);
    
        /**
         * Converts from a json string containing an object to a UStruct
         *
         * @param JsonString String containing JSON formatted data.
         * @param OutStruct The UStruct instance to copy in to
         * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
         * @param SkipFlags Skip properties that match any of these flags
         *
         * @return False if any properties matched but failed to deserialize
         */
        template<typename OutStructType>
        static bool JsonObjectStringToUStruct(const FString& JsonString, OutStructType* OutStruct, int64 CheckFlags, int64 SkipFlags)
        {
            TSharedPtr<FJsonObject> JsonObject;
            TSharedRef<TJsonReader<> > JsonReader = TJsonReaderFactory<>::Create(JsonString);
            if (!FJsonSerializer::Deserialize(JsonReader, JsonObject) || !JsonObject.IsValid())
            {
                UE_LOG(LogJson, Warning, TEXT("JsonObjectStringToUStruct - Unable to parse json=[%s]"), *JsonString);
                return false;
            }
            if (!FJsonObjectConverterEX::JsonObjectToUStruct(JsonObject.ToSharedRef(), OutStruct, CheckFlags, SkipFlags))
            {
                UE_LOG(LogJson, Warning, TEXT("JsonObjectStringToUStruct - Unable to deserialize. json=[%s]"), *JsonString);
                return false;
            }
            return true;
        }
    
        /**
        * Converts from a json string containing an array to an array of UStructs
        *
        * @param JsonString String containing JSON formatted data.
        * @param OutStructArray The UStruct array to copy in to
        * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
        * @param SkipFlags Skip properties that match any of these flags.
        *
        * @return False if any properties matched but failed to deserialize.
        */
        template<typename OutStructType>
        static bool JsonArrayStringToUStruct(const FString& JsonString, TArray<OutStructType>* OutStructArray, int64 CheckFlags, int64 SkipFlags)
        {
            TArray<TSharedPtr<FJsonValue> > JsonArray;
            TSharedRef<TJsonReader<> > JsonReader = TJsonReaderFactory<>::Create(JsonString);
            if (!FJsonSerializer::Deserialize(JsonReader, JsonArray))
            {
                UE_LOG(LogJson, Warning, TEXT("JsonArrayStringToUStruct - Unable to parse. json=[%s]"), *JsonString);
                return false;
            }
            if (!JsonArrayToUStruct(JsonArray, OutStructArray, CheckFlags, SkipFlags))
            {
                UE_LOG(LogJson, Warning, TEXT("JsonArrayStringToUStruct - Error parsing one of the elements. json=[%s]"), *JsonString);
                return false;
            }
            return true;
        }
    
        /**
        * Converts from an array of json values to an array of UStructs.
        *
        * @param JsonArray Array containing json values to convert.
        * @param OutStructArray The UStruct array to copy in to
        * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.
        * @param SkipFlags Skip properties that match any of these flags.
        *
        * @return False if any of the matching elements are not an object, or if one of the matching elements could not be converted to the specified UStruct type.
        */
        template<typename OutStructType>
        static bool JsonArrayToUStruct(const TArray<TSharedPtr<FJsonValue>>& JsonArray, TArray<OutStructType>* OutStructArray, int64 CheckFlags, int64 SkipFlags)
        {
            OutStructArray->SetNum(JsonArray.Num());
            for (int32 i = 0; i < JsonArray.Num(); ++i)
            {
                const auto& Value = JsonArray;
                if (Value->Type != EJson::Object)
                {
                    UE_LOG(LogJson, Warning, TEXT("JsonArrayToUStruct - Array element [%i] was not an object."), i);
                    return false;
                }
                if (!FJsonObjectConverterEX::JsonObjectToUStruct(Value->AsObject().ToSharedRef(), OutStructType::StaticStruct(), &(*OutStructArray), CheckFlags, SkipFlags))
                {
                    UE_LOG(LogJson, Warning, TEXT("JsonArrayToUStruct - Unable to convert element [%i]."), i);
                    return false;
                }
            }
            return true;
        }
    
        /*
        * Parses text arguments from Json into a map
        * @param JsonObject Object to parse arguments from
        */
        static FFormatNamedArguments ParseTextArgumentsFromJson(const TSharedPtr<const FJsonObject>& JsonObject);
    };
    JsonObjectConverterEX.h

     

    // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
    
    #include "JsonObjectConverterEx.h"
    #include "Internationalization/Culture.h"
    #include "UObject/ObjectMacros.h"
    #include "UObject/Class.h"
    #include "UObject/UnrealType.h"
    #include "UObject/EnumProperty.h"
    #include "UObject/TextProperty.h"
    #include "UObject/PropertyPortFlags.h"
    #include "UObject/Package.h"
    #include "Policies/CondensedJsonPrintPolicy.h"
    #include "JsonObjectWrapper.h"
    
    FString FJsonObjectConverterEX::StandardizeCase(const FString &StringIn)
    {
        // this probably won't work for all cases, consider downcaseing the string fully
        FString FixedString = StringIn;
        FixedString[0] = FChar::ToLower(FixedString[0]); // our json classes/variable start lower case
        FixedString.ReplaceInline(TEXT("ID"), TEXT("Id"), ESearchCase::CaseSensitive); // Id is standard instead of ID, some of our fnames use ID
        return FixedString;
    }
    
    
    namespace
    {
    /** Convert property to JSON, assuming either the property is not an array or the value is an individual array element */
    TSharedPtr<FJsonValue> ConvertScalarUPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const FJsonObjectConverterEX::CustomExportCallback* ExportCb)
    {
        // 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 (UEnumProperty* EnumProperty = Cast<UEnumProperty>(Property))
        {
            // export enums as strings
            UEnum* EnumDef = EnumProperty->GetEnum();
            FString StringValue = EnumDef->GetNameStringByValue(EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(Value));
            return MakeShared<FJsonValueString>(StringValue);
        }
        else if (UNumericProperty *NumericProperty = Cast<UNumericProperty>(Property))
        {
            // see if it's an enum
            UEnum* EnumDef = NumericProperty->GetIntPropertyEnum();
            if (EnumDef != NULL)
            {
                // export enums as strings
                FString StringValue = EnumDef->GetNameStringByValue(NumericProperty->GetSignedIntPropertyValue(Value));
                return MakeShared<FJsonValueString>(StringValue);
            }
    
            // 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 (UBoolProperty *BoolProperty = Cast<UBoolProperty>(Property))
        {
            // Export bools as bools
            return MakeShared<FJsonValueBoolean>(BoolProperty->GetPropertyValue(Value));
        }
        else if (UStrProperty *StringProperty = Cast<UStrProperty>(Property))
        {
            return MakeShared<FJsonValueString>(StringProperty->GetPropertyValue(Value));
        }
        else if (UTextProperty *TextProperty = Cast<UTextProperty>(Property))
        {
            return MakeShared<FJsonValueString>(TextProperty->GetPropertyValue(Value).ToString());
        }
        else if (UArrayProperty *ArrayProperty = Cast<UArrayProperty>(Property))
        {
            TArray< TSharedPtr<FJsonValue> > Out;
            FScriptArrayHelper Helper(ArrayProperty, Value);
            for (int32 i=0, n=Helper.Num(); i<n; ++i)
            {
                TSharedPtr<FJsonValue> Elem = FJsonObjectConverterEX::UPropertyToJsonValue(ArrayProperty->Inner, Helper.GetRawPtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb);
                if ( Elem.IsValid() )
                {
                    // add to the array
                    Out.Push(Elem);
                }
            }
            return MakeShared<FJsonValueArray>(Out);
        }
        else if ( USetProperty* SetProperty = Cast<USetProperty>(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 = FJsonObjectConverterEX::UPropertyToJsonValue(SetProperty->ElementProp, Helper.GetElementPtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb);
                    if ( Elem.IsValid() )
                    {
                        // add to the array
                        Out.Push(Elem);
                    }
    
                    --n;
                }
            }
            return MakeShared<FJsonValueArray>(Out);
        }
        else if ( UMapProperty* MapProperty = Cast<UMapProperty>(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 = FJsonObjectConverterEX::UPropertyToJsonValue(MapProperty->KeyProp, Helper.GetKeyPtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb);
                    TSharedPtr<FJsonValue> ValueElement = FJsonObjectConverterEX::UPropertyToJsonValue(MapProperty->ValueProp, Helper.GetValuePtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb);
                    if ( KeyElement.IsValid() && ValueElement.IsValid() )
                    {
                        FString KeyString;
                        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);
                            }
                        }
    
                        Out->SetField(KeyString, ValueElement);
                    }
    
                    --n;
                }
            }
    
            return MakeShared<FJsonValueObject>(Out);
        }
        else if (UStructProperty *StructProperty = Cast<UStructProperty>(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 (FJsonObjectConverterEX::UStructToJsonObject(StructProperty->Struct, Value, Out, CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb))
            {
                return MakeShared<FJsonValueObject>(Out);
            }
            // fall through to default
        }
        else
        {
            // Default to export as string for everything else
            FString StringValue;
            Property->ExportTextItem(StringValue, Value, NULL, NULL, PPF_None);
            return MakeShared<FJsonValueString>(StringValue);
        }
    
        // invalid
        return TSharedPtr<FJsonValue>();
    }
    }
    
    TSharedPtr<FJsonValue> FJsonObjectConverterEX::ObjectJsonCallback(UProperty* Property, const void* Value)
    {
        if (UObjectProperty* ObjectProperty = Cast<UObjectProperty>(Property))
        {
            if (!ObjectProperty->HasAnyFlags(RF_Transient)) // We are taking Transient to mean we don't want to serialize to Json either (could make a new flag if nessasary)
            {
                TSharedRef<FJsonObject> Out = MakeShared<FJsonObject>();
    
                CustomExportCallback CustomCB;
                CustomCB.BindStatic(FJsonObjectConverterEX::ObjectJsonCallback);
    
                void** PtrToValuePtr = (void**)Value;
    
                if (FJsonObjectConverterEX::UStructToJsonObject(ObjectProperty->PropertyClass, (*PtrToValuePtr), Out, 0, 0, &CustomCB))
                {
                    return MakeShared<FJsonValueObject>(Out);
                }
            }
        }
    
        // invalid
        return TSharedPtr<FJsonValue>();
    }
    
    TSharedPtr<FJsonValue> FJsonObjectConverterEX::UPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb)
    {
        if (Property->ArrayDim == 1)
        {
            return ConvertScalarUPropertyToJsonValue(Property, Value, CheckFlags, SkipFlags, ExportCb);
        }
    
        TArray< TSharedPtr<FJsonValue> > Array;
        for (int Index = 0; Index != Property->ArrayDim; ++Index)
        {
            Array.Add(ConvertScalarUPropertyToJsonValue(Property, (char*)Value + Index * Property->ElementSize, CheckFlags, SkipFlags, ExportCb));
        }
        return MakeShared<FJsonValueArray>(Array);
    }
    
    bool FJsonObjectConverterEX::UStructToJsonObject(const UStruct* StructDefinition, const void* Struct, TSharedRef<FJsonObject> OutJsonObject, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb)
    {
        return UStructToJsonAttributes(StructDefinition, Struct, OutJsonObject->Values, CheckFlags, SkipFlags, ExportCb);
    }
    
    bool FJsonObjectConverterEX::UStructToJsonAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonValue> >& OutJsonAttributes, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb)
    {
        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;
        }
    
        for (TFieldIterator<UProperty> It(StructDefinition); It; ++It)
        {
            UProperty* 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 = Property->GetName();// StandardizeCase(Property->GetName());
            const void* Value = Property->ContainerPtrToValuePtr<uint8>(Struct);
    
            // convert the property to a FJsonValue
            TSharedPtr<FJsonValue> JsonValue = UPropertyToJsonValue(Property, Value, CheckFlags, SkipFlags, ExportCb);
            if (!JsonValue.IsValid())
            {
                UClass* PropClass = Property->GetClass();
                UE_LOG(LogJson, Error, TEXT("UStructToJsonObject - Unhandled property type '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
                return false;
            }
    
            // set the value on the output object
            OutJsonAttributes.Add(VariableName, JsonValue);
        }
    
        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 FJsonObjectConverterEX::UStructToJsonObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonString, int64 CheckFlags, int64 SkipFlags, int32 Indent, const CustomExportCallback* ExportCb, bool bPrettyPrint)
    {
        TSharedRef<FJsonObject> JsonObject = MakeShared<FJsonObject>();
        if (UStructToJsonObject(StructDefinition, Struct, JsonObject, CheckFlags, SkipFlags, ExportCb))
        {
            bool bSuccess = false;
            if (bPrettyPrint)
            {
                bSuccess = UStructToJsonObjectStringInternal<TCHAR, TPrettyJsonPrintPolicy<TCHAR> >(JsonObject, OutJsonString, Indent);
            }
            else
            {
                bSuccess = UStructToJsonObjectStringInternal<TCHAR, TCondensedJsonPrintPolicy<TCHAR> >(JsonObject, OutJsonString, Indent);
            }
            if (bSuccess)
            {
                return true;
            }
            else
            {
                UE_LOG(LogJson, Warning, TEXT("UStructToJsonObjectString - Unable to write out json"));
            }
        }
    
        return false;
    }
    
    //static
    bool FJsonObjectConverterEX::GetTextFromObject(const TSharedRef<FJsonObject>& Obj, FText& TextOut)
    {
        // get the prioritized culture name list
        FCultureRef CurrentCulture = FInternationalization::Get().GetCurrentCulture();
        TArray<FString> CultureList = CurrentCulture->GetPrioritizedParentCultureNames();
    
        // try to follow the fall back chain that the engine uses
        FString TextString;
        for (const FString& CultureCode : CultureList)
        {
            if (Obj->TryGetStringField(CultureCode, TextString))
            {
                TextOut = FText::FromString(TextString);
                return true;
            }
        }
    
        // try again but only search on the locale region (in the localized data). This is a common omission (i.e. en-US source text should be used if no en is defined)
        for (const FString& LocaleToMatch : CultureList)
        {
            int32 SeparatorPos;
            // only consider base language entries in culture chain (i.e. "en")
            if (!LocaleToMatch.FindChar('-', SeparatorPos))
            {
                for (const auto& Pair : Obj->Values)
                {
                    // only consider coupled entries now (base ones would have been matched on first path) (i.e. "en-US")
                    if (Pair.Key.FindChar('-', SeparatorPos))
                    {
                        if (Pair.Key.StartsWith(LocaleToMatch))
                        {
                            TextOut = FText::FromString(Pair.Value->AsString());
                            return true;
                        }
                    }
                }
            }
        }
    
        // no luck, is this possibly an unrelated json object?
        return false;
    }
    
    
    namespace
    {
        bool JsonValueToUPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, UProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
        bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
    
        /** Convert JSON to property, assuming either the property is not an array or the value is an individual array element */
        bool ConvertScalarJsonValueToUPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, UProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
        {
            if (UEnumProperty* EnumProperty = Cast<UEnumProperty>(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, Error, TEXT("JsonValueToUProperty - Unable import enum %s from string value %s for property %s"), *Enum->CppType, *StrValue, *Property->GetNameCPP());
                        return false;
                    }
                    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 (UNumericProperty *NumericProperty = Cast<UNumericProperty>(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();
                    int64 IntValue = Enum->GetValueByName(FName(*StrValue));
                    if (IntValue == INDEX_NONE)
                    {
                        UE_LOG(LogJson, Error, TEXT("JsonValueToUProperty - Unable import enum %s from string value %s for property %s"), *Enum->CppType, *StrValue, *Property->GetNameCPP());
                        return false;
                    }
                    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 (UBoolProperty *BoolProperty = Cast<UBoolProperty>(Property))
            {
                // AsBool will log an error for completely inappropriate types (then give us a default)
                BoolProperty->SetPropertyValue(OutValue, JsonValue->AsBool());
            }
            else if (UStrProperty *StringProperty = Cast<UStrProperty>(Property))
            {
                // AsString will log an error for completely inappropriate types (then give us a default)
                StringProperty->SetPropertyValue(OutValue, JsonValue->AsString());
            }
            else if (UArrayProperty *ArrayProperty = Cast<UArrayProperty>(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;
                        if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
                        {
                            if (!JsonValueToUPropertyWithContainer(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 (UMapProperty* MapProperty = Cast<UMapProperty>(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 = JsonValueToUPropertyWithContainer(TempKeyValue, MapProperty->KeyProp, Helper.GetKeyPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
                            const bool bValueSuccess = JsonValueToUPropertyWithContainer(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 (USetProperty* SetProperty = Cast<USetProperty>(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;
                        if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
                        {
                            int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
                            if (!JsonValueToUPropertyWithContainer(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 (UTextProperty* TextProperty = Cast<UTextProperty>(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 (!FJsonObjectConverterEX::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 (UStructProperty *StructProperty = Cast<UStructProperty>(Property))
            {
                static const FName NAME_DateTime(TEXT("DateTime"));
                static const FName NAME_Color(TEXT("Color"));
                static const FName NAME_LinearColor(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_LinearColor)
                {
                    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_Color)
                {
                    FColor& ColorOut = *(FColor*)OutValue;
                    FString ColorString = JsonValue->AsString();
    
                    ColorOut = FColor::FromHex(ColorString);
                }
                else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_DateTime)
                {
                    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
                        Property->ImportText(ImportTextPtr, OutValue, PPF_None, nullptr);
                    }
                }
                else if (JsonValue->Type == EJson::String)
                {
                    FString ImportTextString = JsonValue->AsString();
                    const TCHAR* ImportTextPtr = *ImportTextString;
                    Property->ImportText(ImportTextPtr, OutValue, PPF_None, nullptr);
                }
                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 (UObjectProperty *ObjectProperty = Cast<UObjectProperty>(Property))
            {
                if (JsonValue->Type == EJson::Object)
                {
                    UObject* Outer = GetTransientPackage();
                    if (ContainerStruct->IsChildOf(UObject::StaticClass()))
                    {
                        Outer = (UObject*)Container;
                    }
    
                    UClass* PropertyClass = ObjectProperty->PropertyClass;
                    UObject* createdObj = StaticAllocateObject(PropertyClass, Outer, NAME_None, EObjectFlags::RF_NoFlags, EInternalObjectFlags::None, false);
                    (*PropertyClass->ClassConstructor)(FObjectInitializer(createdObj, PropertyClass->ClassDefaultObject, false, false));
    
                    ObjectProperty->SetObjectPropertyValue(OutValue, createdObj);
    
                    TSharedPtr<FJsonObject> Obj = JsonValue->AsObject();
                    check(Obj.IsValid()); // should not fail if Type == EJson::Object
                    if (!JsonAttributesToUStructWithContainer(Obj->Values, ObjectProperty->PropertyClass, createdObj, ObjectProperty->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 (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;
                    }
                }
            }
            else
            {
                // Default to expect a string for everything 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;
                }
            }
    
            return true;
        }
    
    
        bool JsonValueToUPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, UProperty* 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<UArrayProperty>() || Property->IsA<USetProperty>();
            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 ConvertScalarJsonValueToUPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
            }
    
            // In practice, the ArrayDim == 1 check ought to be redundant, since nested arrays of UPropertys are not supported
            if (bArrayOrSetProperty && Property->ArrayDim == 1)
            {
                // Read into TArray
                return ConvertScalarJsonValueToUPropertyWithContainer(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 (!ConvertScalarJsonValueToUPropertyWithContainer(ArrayValue[Index], Property, (char*)OutValue + Index * Property->ElementSize, ContainerStruct, Container, CheckFlags, SkipFlags))
                {
                    return false;
                }
            }
            return true;
        }
    
        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<UProperty> PropIt(StructDefinition); PropIt; ++PropIt)
            {
                UProperty* 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());
                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 (!JsonValueToUPropertyWithContainer(*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 FJsonObjectConverterEX::JsonValueToUProperty(const TSharedPtr<FJsonValue>& JsonValue, UProperty* Property, void* OutValue, int64 CheckFlags, int64 SkipFlags)
    {
        return JsonValueToUPropertyWithContainer(JsonValue, Property, OutValue, nullptr, nullptr, CheckFlags, SkipFlags);
    }
    
    bool FJsonObjectConverterEX::JsonObjectToUStruct(const TSharedRef<FJsonObject>& JsonObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags)
    {
        return JsonAttributesToUStruct(JsonObject->Values, StructDefinition, OutStruct, CheckFlags, SkipFlags);
    }
    
    bool FJsonObjectConverterEX::JsonAttributesToUStruct(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags)
    {
        return JsonAttributesToUStructWithContainer(JsonAttributes, StructDefinition, OutStruct, StructDefinition, OutStruct, CheckFlags, SkipFlags);
    }
    
    FFormatNamedArguments FJsonObjectConverterEX::ParseTextArgumentsFromJson(const TSharedPtr<const FJsonObject>& JsonObject)
    {
        FFormatNamedArguments NamedArgs;
        if (JsonObject.IsValid())
        {
            for (const auto& It : JsonObject->Values)
            {
                if (!It.Value.IsValid())
                    continue;
    
                switch (It.Value->Type)
                {
                case EJson::Number:
                    // number
                    NamedArgs.Emplace(It.Key, It.Value->AsNumber());
                    break;
                case EJson::String:
                    // culture invariant string
                    NamedArgs.Emplace(It.Key, FText::FromString(It.Value->AsString()));
                    break;
                case EJson::Object:
                {
                    // localized string
                    FText TextOut;
                    if (FJsonObjectConverterEX::GetTextFromObject(It.Value->AsObject().ToSharedRef(), TextOut))
                    {
                        NamedArgs.Emplace(It.Key, TextOut);
                    }
                    else
                    {
                        UE_LOG(LogJson, Error, TEXT("Unable to apply Json parameter %s (could not parse object)"), *It.Key);
                    }
                }
                break;
                default:
                    UE_LOG(LogJson, Error, TEXT("Unable to apply Json parameter %s (bad type)"), *It.Key);
                    break;
                }
            }
        }
        return NamedArgs;
    }
    JsonObjectConverterEX.cpp

     

        FString ret_json;
        FDTOData_Test data;
        
        data.Title = TEXT("sssss");
        FJsonObjectConverterEX::UStructToJsonObjectString(data , ret_json, 0, 0);
        /*
            {
                "Title" : "sssss"
            }
        */

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 21:27 , Processed in 0.063295 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表