蓝图创建Structure

测试读JSON:

测试写JSON

结果:

因为代码比较长,所以先解释一下,最后在放完整代码。

代码解析:

一、把蓝图Structure暴露到C++

UFUNCTION(BlueprintCallable, Category = "Json", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
    static bool ReadJson(const FString& JsonStr, UStructProperty* AnyStruct);

DECLARE_FUNCTION(execReadJson) {
        P_GET_PROPERTY(UStrProperty, JsonStr);
        Stack.Step(Stack.Object, NULL);
        UStructProperty* StructProperty = ExactCast<UStructProperty>(Stack.MostRecentProperty);
        void* StructPtr = Stack.MostRecentPropertyAddress;
        P_FINISH;

*(bool*)RESULT_PARAM = JsonStrToStruct(StructProperty, StructPtr, JsonStr);

}

官方文档的说明:

CustomThunk

UnrealHeaderTool 代码生成器将不为此函数生成thunk,用户需要提供一个thunk。

CustomStructureParam="Parameter1, Parameter2, ..")

列出的参数都会被视为通配符。此说明符需要 UFUNCTION-level specifier、CustomThunk,而它们又需要用户提供自定义的 exec 函数。在此函数中,可对参数类型进行检查,可基于这些参数类型执行合适的函数调用。永不应调用基础 UFUNCTION,出现错误时应进行断言或记录。

要声明自定义 exec 函数,使用语法 DECLARE_FUNCTION(execMyFunctionName)MyFunctionName 为原函数命名。

这样,我们就能在自己C++代码里解析蓝图的结构体了。

二、UStruct与JSON的转换

JsonUtilities模块的FJsonObjectConverter类里有接口进行UStruct到Json的互相转换,但是只能是C++类,不能是蓝图结构体。原因其实很简单。

下面这个是FJsonObjectConverter类的JsonAttributesToUStructWithContainer函数 :

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;
    }

当传入的是蓝图的结构体时,Property->GetName()这个其实是返回变量名加后缀的,也就是因为多了后缀JsonAttributes.Find找不到同名的参数,所以把改成去掉后缀的名字就能够找到。在源码里把这些改了就会发现可以读到了。

不过我懒得改源码。所以把相关的都复制黏贴到自己的类里修改后直接调用自己类里的。

所以其实主要功能就是复制FJsonObjectConverter里改的,而且就是把Property->GetName()的地方改下而已。

即以下函数

JsonAttributesToUStructWithContainer

UStructToJsonAttributes

JsonValueToUPropertyWithContainer

ConvertScalarJsonValueToUPropertyWithContainer

UPropertyToJsonValue

ConvertScalarUPropertyToJsonValue

C++代码

头文件:

#pragma once#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "JsonOperateLibrary.generated.h"class FJsonValue;/*** */
UCLASS()
class MY_API UJsonOperateLibrary :public UBlueprintFunctionLibrary
{GENERATED_BODY()public:UFUNCTION(BlueprintCallable, Category = "Json", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))static bool ReadJson(const FString& JsonStr, UStructProperty* AnyStruct);DECLARE_FUNCTION(execReadJson) {P_GET_PROPERTY(UStrProperty, JsonStr);Stack.Step(Stack.Object, NULL);UStructProperty* StructProperty = ExactCast<UStructProperty>(Stack.MostRecentProperty);void* StructPtr = Stack.MostRecentPropertyAddress;P_FINISH;*(bool*)RESULT_PARAM = JsonStrToStruct(StructProperty, StructPtr, JsonStr);}UFUNCTION(BlueprintCallable, Category = "Json", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))static bool WriteJson( UStructProperty* AnyStruct,FString& JsonStr);DECLARE_FUNCTION(execWriteJson) {Stack.Step(Stack.Object, NULL);UStructProperty* StructProperty = ExactCast<UStructProperty>(Stack.MostRecentProperty);void* StructPtr = Stack.MostRecentPropertyAddress;P_GET_PROPERTY_REF(UStrProperty, JsonStr);P_FINISH;*(bool*)RESULT_PARAM = StructToJsonStr(StructProperty, StructPtr,JsonStr);}static bool JsonStrToStruct(UStructProperty* StructProperty, void* StructPtr, const FString& InString);static bool StructToJsonStr(UStructProperty* StructProperty, void* StructPtr, FString& InString);private:static FString GetShortName(UProperty* Property);static bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonValue> >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);static bool UStructToJsonAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonValue> >& OutJsonAttributes, int64 CheckFlags, int64 SkipFlags);static bool JsonValueToUPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, UProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);static bool ConvertScalarJsonValueToUPropertyWithContainer(const TSharedPtr<FJsonValue>& JsonValue, UProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);static TSharedPtr<FJsonValue> UPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags);static TSharedPtr<FJsonValue> ConvertScalarUPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags);
};

cpp文件:

// Fill out your copyright notice in the Description page of Project Settings.#include "JsonOperateLibrary.h"
#include "Json.h"
#include "JsonUtilities.h"
#include "UObject/EnumProperty.h"
#include "UObject/TextProperty.h"
#include "UObject/Package.h"
#include "UObject/ObjectMacros.h"
#include "JsonObjectConverter.h"bool UJsonOperateLibrary::JsonStrToStruct(UStructProperty* StructProperty, void* StructPtr, const FString& InString)
{UScriptStruct* Struct = StructProperty->Struct;TSharedPtr<FJsonObject> JsonObject;TSharedRef<TJsonReader<> > JsonReader = TJsonReaderFactory<>::Create(InString);if (FJsonSerializer::Deserialize(JsonReader, JsonObject) || !JsonObject.IsValid()){return JsonAttributesToUStructWithContainer(JsonObject->Values, Struct, StructPtr, Struct, StructPtr, 1, 0);}return false;
}bool UJsonOperateLibrary::StructToJsonStr(UStructProperty* StructProperty, void* StructPtr, FString& InString)
{UScriptStruct* Struct = StructProperty->Struct;TSharedRef<FJsonObject> JsonObject = MakeShared<FJsonObject>();if (UStructToJsonAttributes(Struct, StructPtr, JsonObject->Values, 1, 0)){bool bSuccess = false;TSharedRef<TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>> > JsonWriter = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&InString, 0);bSuccess = FJsonSerializer::Serialize(JsonObject, JsonWriter);JsonWriter->Close();if (bSuccess){return true;}else{UE_LOG(LogJson, Warning, TEXT("UStructToJsonObjectString - Unable to write out json"));}}return false;}FString UJsonOperateLibrary::GetShortName(UProperty* Property)
{FString PropertyName = Property->GetName();FString Left;FString Right;if (PropertyName.Split("_", &Left, &Right)){return Left;}return PropertyName;
}bool UJsonOperateLibrary::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 objectFJsonObjectWrapper* 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 propertiesfor (TFieldIterator<UProperty> PropIt(StructDefinition); PropIt; ++PropIt){UProperty* Property = *PropIt;// Check to see if we should ignore this propertyif (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags)){continue;}if (Property->HasAnyPropertyFlags(SkipFlags)){continue;}// find a json value matching this property nameconst TSharedPtr<FJsonValue>* JsonValue = JsonAttributes.Find(GetShortName(Property));if (!JsonValue){// we allow values to not be found since this mirrors the typical UObject mantra that all the fields are optional when deserializingcontinue;}if (JsonValue->IsValid() && !(*JsonValue)->IsNull()){void* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);if (!JsonValueToUPropertyWithContainer(*JsonValue, Property, Value, nullptr, nullptr, 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;
}/** Convert JSON to property, assuming either the property is not an array or the value is an individual array element */
bool UJsonOperateLibrary::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 supportedif (bArrayOrSetProperty && Property->ArrayDim == 1){// Read into TArrayreturn ConvertScalarJsonValueToUPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);}// We're deserializing a JSON arrayconst 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 arrayint 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 UJsonOperateLibrary::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 enumconst 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 enumconst 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 matchFScriptArrayHelper Helper(ArrayProperty, OutValue);Helper.Resize(ArrLen);// set the property valuesfor (int32 i = 0; i < ArrLen; ++i){const TSharedPtr<FJsonValue>& ArrayValueItem = ArrayValue[i];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 valuesfor (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 valuesfor (int32 i = 0; i < ArrLen; ++i){const TSharedPtr<FJsonValue>& ArrayValueItem = ArrayValue[i];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 invariantTextProperty->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 stringFText 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 (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::Objectif (!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 keywordsDateTimeOut = 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, 0x00000000, nullptr, (FOutputDevice*)GWarn)){// Fall back to trying the tagged property approach if custom ImportTextItem couldn't get it doneProperty->ImportText(ImportTextPtr, OutValue, 0x00000000, nullptr);}}else if (JsonValue->Type == EJson::String){FString ImportTextString = JsonValue->AsString();const TCHAR* ImportTextPtr = *ImportTextString;Property->ImportText(ImportTextPtr, OutValue, 0x00000000, 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::Objectif (!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 elseif (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 elseif (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 UJsonOperateLibrary::UStructToJsonAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonValue> >& OutJsonAttributes, int64 CheckFlags, int64 SkipFlags)
{if (SkipFlags == 0){// If we have no specified skip flags, skip deprecated, transient and skip serialization by default when writingSkipFlags |= CPF_Deprecated | CPF_Transient;}if (StructDefinition == FJsonObjectWrapper::StaticStruct()){// Just copy it into the objectconst 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 propertyif (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags)){continue;}if (Property->HasAnyPropertyFlags(SkipFlags)){continue;}FString VariableName = FJsonObjectConverter::StandardizeCase(GetShortName(Property));const void* Value = Property->ContainerPtrToValuePtr<uint8>(Struct);// convert the property to a FJsonValueTSharedPtr<FJsonValue> JsonValue = UPropertyToJsonValue(Property, Value, CheckFlags, SkipFlags);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 objectOutJsonAttributes.Add(VariableName, JsonValue);}return true;
}TSharedPtr<FJsonValue> UJsonOperateLibrary::UPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags)
{if (Property->ArrayDim == 1){return ConvertScalarUPropertyToJsonValue(Property, Value, CheckFlags, SkipFlags);}TArray< TSharedPtr<FJsonValue> > Array;for (int Index = 0; Index != Property->ArrayDim; ++Index){Array.Add(ConvertScalarUPropertyToJsonValue(Property, (char*)Value + Index * Property->ElementSize, CheckFlags, SkipFlags));}return MakeShared<FJsonValueArray>(Array);
}/** Convert property to JSON, assuming either the property is not an array or the value is an individual array element */
TSharedPtr<FJsonValue> UJsonOperateLibrary::ConvertScalarUPropertyToJsonValue(UProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags)
{if (UEnumProperty * EnumProperty = Cast<UEnumProperty>(Property)){// export enums as stringsUEnum* 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 enumUEnum* EnumDef = NumericProperty->GetIntPropertyEnum();if (EnumDef != NULL){// export enums as stringsFString StringValue = EnumDef->GetNameStringByValue(NumericProperty->GetSignedIntPropertyValue(Value));return MakeShared<FJsonValueString>(StringValue);}// We want to export numbers as numbersif (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 boolsreturn 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 = UPropertyToJsonValue(ArrayProperty->Inner, Helper.GetRawPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags);if (Elem.IsValid()){// add to the arrayOut.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 = UPropertyToJsonValue(SetProperty->ElementProp, Helper.GetElementPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags);if (Elem.IsValid()){// add to the arrayOut.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 = UPropertyToJsonValue(MapProperty->KeyProp, Helper.GetKeyPtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags);TSharedPtr<FJsonValue> ValueElement = UPropertyToJsonValue(MapProperty->ValueProp, Helper.GetValuePtr(i), CheckFlags & (~CPF_ParmFlags), SkipFlags);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 stringif (StructProperty->Struct != FJsonObjectWrapper::StaticStruct() && TheCppStructOps && TheCppStructOps->HasExportTextItem()){FString OutValueStr;TheCppStructOps->ExportTextItem(OutValueStr, Value, nullptr, nullptr, 0x00000000, nullptr);return MakeShared<FJsonValueString>(OutValueStr);}TSharedRef<FJsonObject> Out = MakeShared<FJsonObject>();if (UStructToJsonAttributes(StructProperty->Struct, Value, Out->Values, CheckFlags & (~CPF_ParmFlags), SkipFlags)){return MakeShared<FJsonValueObject>(Out);}// fall through to default}else{// Default to export as string for everything elseFString StringValue;Property->ExportTextItem(StringValue, Value, NULL, NULL, 0x00000000);return MakeShared<FJsonValueString>(StringValue);}// invalidreturn TSharedPtr<FJsonValue>();
}

参考:

https://blog.csdn.net/justtestlike/article/details/84300154

https://docs.unrealengine.com/en-US/API/Runtime/JsonUtilities/FJsonObjectConverter/index.html

https://blog.csdn.net/u012999985/article/details/52902065

https://docs.unrealengine.com/zh-CN/Programming/UnrealArchitecture/Reference/Functions/Specifiers/index.html

UE4 蓝图Structure与Json的读写相关推荐

  1. UE4 蓝图Structure与Xml的读取

    使用了Unreal提供的XmlParser库来解析Xml文件.然后把解析到的内容填充到定义好的结构体内.该方式是以蓝图Structure来定义结构. 使用: 一个简单的xml文件 <?xml v ...

  2. UE4蓝图无代码编程游戏开发技能学习教程

    在虚幻引擎4中创建.设计和开发自己的游戏,无需编码 你会学到什么 虚幻引擎4中使用蓝图的游戏开发(无代码编程) 使用行业标准方法的游戏设计 使用Maya进行三维设计 在本课程中创建您的第一个游戏 Ga ...

  3. UE4蓝图中的AI行为树功能使用经验指南

    ue4AI和行为树(Blueprint) 该篇是网友yangxuan0261学习ue4的一片学习笔记,里面有一些需要注意的设置点,跟官方文档上所讲少有不同,请大家避免掉坑. 简单目标:指定一个TARg ...

  4. JSON定义及解析,JSON文件读写

    文章目录 JSON 定义 要点 简单的JSON实例 JSON的文档结构 对象 数组 数据类型 值 字符串说明 关于轨道图的嵌套 JSON实例 使用JsonCpp进行JSON文件读写 类图关系 常用接口 ...

  5. 蓝图解锁怎么用_[UE4蓝图][Materials]虚幻4中可互动的雪地材质完整实现(一)

    不说废话,先上个演示图 最终成果(脚印,雪地可慢慢恢复,地形可控制) 主要原理(白话文): 假如你头上是块白色并且可以透视的平地,来了个非洲兄弟踩上面,你拿起单反对着上面拍了一张,照片如下 把脚印稍作 ...

  6. ue4 怎么传递变量到另一个蓝图_[UE4蓝图]虚幻4中实现简易天气系统(三)—— 受风力影响的Cascade雨水粒子...

    上一篇: 架狙只打脚:[UE4蓝图]虚幻4中实现简易天气系统(二)-- 随机风力​zhuanlan.zhihu.com Cascade就是现在UE4中正在使用的ParticleSystem. 制作雨水 ...

  7. ue4蓝图运行顺序_UE4蓝图解析(四)

    这是蓝图解析系列文章的第四部分,将介绍Statement优化和字节码生成 相关索引:南京周润发:UE4蓝图解析(一)​zhuanlan.zhihu.com南京周润发:UE4蓝图解析(二)​zhuanl ...

  8. ue4蓝图运行顺序_学习UE4,先学蓝图还是c++_资源库

    由于UE4编程从语言上划分为c++和蓝图脚本,所以学习UE4的人都会在蓝图和c++之中摇摆不定.下面为大家分享UE4蓝图和c++各自的优势与实用性,大家可以根据这些信息酌情选择先学习蓝图还是c++. ...

  9. 《UE4蓝图完全学习》笔记

    UE4蓝图完全学习教程笔记 文章目录 UE4蓝图完全学习教程笔记 Chapter 1 & 2 1. 打印字符串:print(printstring) 2. 创建各种类型的文本:make lit ...

最新文章

  1. java 获取java文件路径_Java怎么获取相对路径下所有的.java文件的信息
  2. python dlib opencv人脸识别准确度_Python用opencv+dlib实现人脸识别
  3. JVM 对象状态判断01
  4. RPC创建API 模块
  5. pandas处理大数据的一些小技巧
  6. 【渝粤教育】国家开放大学2018年春季 0556-21T广告摄影 参考试题
  7. python爬虫 正则表达式 re.finditer 元字符 贪婪匹配 惰性匹配
  8. 【QT】无需写connect代码关联信号和槽函数
  9. matlab中对正弦信号采样,正弦信号抽样的实验报告(共9篇).doc
  10. 为什么重写equals方法时,要求必须重写hashCode方法?
  11. turbo c 2.0 官方下载_iMyFone MintPDF英文版官方下载2.0.10
  12. 模拟电子技术知识点总结
  13. 汉字转拼音开源工具包Jpinyin介绍
  14. Ubuntu 设置固定ip地址
  15. HealthKit框架简介
  16. Android O (8.0) 新特性介绍
  17. git远程代码回滚_git 远程分支回滚
  18. HDU4622- Reincarnation(后缀自动机)
  19. 中文菜单的html编辑器,如何更改IE查看源代码菜单使用的HTML编辑器
  20. win7或win2008系统中,出现【已停止工作,联机检查解决方案并关闭该程序,关闭程序】解决方法!

热门文章

  1. 山东大学-飞桨人工智能教育创新中心正式挂牌,打造区域产教融合新范式
  2. 如何让开源项目成为你的良师益友
  3. OpenCV 的人脸detect及PCA匹配
  4. 回归生活:清理微信公众号
  5. Linux 负载均衡介绍之LB介绍
  6. 微型计算机系统实验总结(学习性实验:IO地址译码,可编程并行接口8255,交通灯控制实验 + 自主设计实验:汽车信号灯控制系统,电风扇控制器,洗衣机控制系统,霓虹灯,电梯控制系统)
  7. 智能门锁:电源管理概述2
  8. 头条二面智力题!难受!
  9. 笔记:springboot-admin 整合spring security应用注册失败问题
  10. Pycharm下运行调试Python项目时,当调试既需要给调试的程序传入命令行参数又需要程序在设置的断点处停下里查看变量时的解决方法