UE4 藍圖Structure與Xml的讀取

 

使用了Unreal提供的XmlParser庫來解析Xml文件。然後把解析到的內容填充到定義好的結構體內。該方式是以藍圖Structure來定義結構。

 

使用:

一個簡單的xml文件

<?xml version="1.0" encoding="UTF-8"?>
<test>
	<testa id="0" name="aaaaaaa" other="other">
		<testb id="0" name="a000" desc="wef">
		acontent
		</testb>
		<testb id="1" name="a111" desc="ewfwe"></testb>
		<testb id="2" name="a222" desc="456fwe"></testb>
		<testb id="3" name="a333" desc=""></testb>
	</testa>
	<testa id="1" name="bbbbbbb" other="">
		<testb id="0" name="b000" desc=""></testb>
		<testb id="1" name="b111" desc="fewfwe"></testb>
		<testb id="2" name="b222" ></testb>
		<testb id="3" name="b333" desc=""></testb>
	</testa>
</test>

藍圖Structure定義:

testb節點定義:

testa節點定義:

整個xml的節點定義:

 

注意:定義結構體有幾個注意點。

1.attribute的參數名直接以xml裏的名稱定義(區分大小寫)。

2.子節點的參數名以childNode定義(不區分大小寫)定義成數組。

3.內容文本的參數名以content定義(不區分大小寫)。

4.childNode數組也不用填值,會按照xml的子節點數量填入

 

測試:

結果:


 

代碼:

代碼看不明白可以參考我之前一篇Json讀寫的,裏面有一些解釋 

https://blog.csdn.net/u013507300/article/details/102487642

 

頭文件:

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "XmlOperateLibrary.generated.h"

PT_API DECLARE_LOG_CATEGORY_EXTERN(LogXml, Log, All);
class FXmlNode;
/**
 * 
 */
UCLASS()
class MY_API UXmlOperateLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
	UFUNCTION(BlueprintCallable, Category = "Xml", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
	static bool ReadXml(const FString& xmlPath, UStructProperty* AnyStruct);

	DECLARE_FUNCTION(execReadXml)
	{
		P_GET_PROPERTY(UStrProperty, xmlPath);

		// Steps into the stack, walking to the next property in it
		Stack.Step(Stack.Object, NULL);

		// Grab the last property found when we walked the stack
		// This does not contains the property value, only its type information
		UStructProperty* StructProperty = ExactCast<UStructProperty>(Stack.MostRecentProperty);

		// Grab the base address where the struct actually stores its data
		// This is where the property value is truly stored
		void* StructPtr = Stack.MostRecentPropertyAddress;

		// We need this to wrap up the stack
		P_FINISH;

		*(bool*) RESULT_PARAM = XmlStrToStruct(StructProperty, StructPtr, xmlPath);
	}

	static bool XmlStrToStruct(UStructProperty* StructProperty, void* StructPtr, const FString& xmlPath);

	static bool XmlNodeToStructWithContainer(const FXmlNode* node, const UStruct* StructDefinition, void* OutStruct);

	static bool XmlNodeToUPropertyWithContainer(const FXmlNode* node, UProperty* Property, void* OutValue);

	static bool XmlValueToUPropertyWithContainer(const FXmlNode* node, UProperty* Property, const FString& PropertyName, void* OutValue);

	static bool ConvertScalarXmlValueToUPropertyWithContainer(const FString& XmlValue, UProperty* Property, void* OutValue);

	static FString GetShortName(UProperty* Property);
};

cpp文件:

// Fill out your copyright notice in the Description page of Project Settings.

#include "XmlOperateLibrary.h"

#include "HAL/PlatformFilemanager.h"
#include "XmlParser.h"

DEFINE_LOG_CATEGORY(LogXml);

bool UXmlOperateLibrary::XmlStrToStruct(UStructProperty* StructProperty, void* StructPtr, const FString& xmlPath)
{
	if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*(xmlPath)))	  //判斷是否有此文件
	{
		UE_LOG(LogXml, Error, TEXT("Unable find xml file : %s "), *xmlPath);
		return false;
	}

	FXmlFile* _XmlFile = new FXmlFile(xmlPath);
	if (_XmlFile == nullptr)
	{
		UE_LOG(LogXml, Error, TEXT("Unable load xml file: %s "), *xmlPath);
		return false;
	}

	FXmlNode* _RootNode = _XmlFile->GetRootNode();

	if (_RootNode == nullptr)
	{
		UE_LOG(LogXml, Error, TEXT("Unable find xml root node: %s "), *xmlPath);
		return false;
	}

	UScriptStruct* Struct = StructProperty->Struct;

	XmlNodeToStructWithContainer(_RootNode, Struct, StructPtr);

	return true;
}

bool UXmlOperateLibrary::XmlNodeToStructWithContainer(const FXmlNode* node, const UStruct* StructDefinition, void* OutStruct)
{
	for (TFieldIterator<UProperty> PropIt(StructDefinition); PropIt; ++PropIt)
	{
		UProperty* Property = *PropIt;
		FString shortName = GetShortName(Property);

		if (shortName.ToLower().Equals("childnode"))
		{
			const TArray<FXmlNode*> Children = node->GetChildrenNodes();
			const int32 ChildCount = Children.Num();
			for (int32 ChildIndex = 0; ChildIndex < ChildCount; ++ChildIndex)
			{
				if (Children[ChildIndex] != nullptr)
				{
					void* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);
					XmlNodeToUPropertyWithContainer(node, Property, Value);
				}
			}
		}
		else if (shortName.ToLower().Equals("content"))
		{
			if (UStrProperty* StringProperty = Cast<UStrProperty>(Property))
			{
				void* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);
				StringProperty->SetPropertyValue(Value, node->GetContent());
			}
			else
			{
				UE_LOG(LogXml, Error, TEXT("XmlContent Property type error : Property Type must be string"));
				return false;
			}
		}
		else
		{
			void* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);
			XmlValueToUPropertyWithContainer(node, Property, shortName, Value);
		}
	}

	return true;
}

bool UXmlOperateLibrary::XmlNodeToUPropertyWithContainer(const FXmlNode* node, UProperty* Property, void* OutValue)
{
	if (UStructProperty* StructProperty = Cast<UStructProperty>(Property))
	{
		return XmlNodeToStructWithContainer(node, StructProperty->Struct, OutValue);
	}
	else if (UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property))
	{
		const TArray<FXmlNode*> _AssetNodes = node->GetChildrenNodes();
		int i = 0;
		// make the output array size match
		FScriptArrayHelper Helper(ArrayProperty, OutValue);
		Helper.Resize(_AssetNodes.Num());
		for (FXmlNode* childNode : _AssetNodes)
		{
			XmlNodeToUPropertyWithContainer(childNode, ArrayProperty->Inner, Helper.GetRawPtr(i));
			i++;
		}
	}
	else if (UObjectProperty* ObjectProperty = Cast<UObjectProperty>(Property))
	{
		UE_LOG(LogXml, Error, TEXT(" Unable use UObjectProperty Type for  Property  %s"), *Property->GetNameCPP());
		return false;
	}
	else if (UMapProperty* MapProperty = Cast<UMapProperty>(Property))
	{
		UE_LOG(LogXml, Error, TEXT(" Unable use UMapProperty Type for  Property  %s"), *Property->GetNameCPP());
		return false;
	}
	else if (USetProperty* SetProperty = Cast<USetProperty>(Property))
	{
		UE_LOG(LogXml, Error, TEXT(" Unable use USetProperty Type for  Property  %s"), *Property->GetNameCPP());
		return false;
	}
	else
	{
		UE_LOG(LogXml, Error, TEXT(" Unable use this Type for  Property  %s"), *Property->GetNameCPP());
		return false;
	}

	return true;
}

bool UXmlOperateLibrary::XmlValueToUPropertyWithContainer(
	const FXmlNode* node, UProperty* Property, const FString& PropertyName, void* OutValue)
{
	TArray<FXmlAttribute> attribute = node->GetAttributes();
	bool result = false;
	for (int i = 0; i < attribute.Num(); i++)
	{
		if (PropertyName.Equals(attribute[i].GetTag()))
		{
			if (ConvertScalarXmlValueToUPropertyWithContainer(attribute[i].GetValue(), Property, OutValue))
			{
				result = true;
			}
			break;
		}
	}
	return result;
}

bool UXmlOperateLibrary::ConvertScalarXmlValueToUPropertyWithContainer(const FString& XmlValue, UProperty* Property, void* OutValue)
{
	if (UEnumProperty* EnumProperty = Cast<UEnumProperty>(Property))
	{
		if (!FCString::IsNumeric(*XmlValue))
		{
			const UEnum* Enum = EnumProperty->GetEnum();
			check(Enum);
			int64 IntValue = Enum->GetValueByName(FName(*XmlValue));
			if (IntValue == INDEX_NONE)
			{
				UE_LOG(LogXml, Error, TEXT("XmlValueToUProperty - Unable import enum %s from string value %s for property %s"),
					*Enum->CppType, *XmlValue, *Property->GetNameCPP());
				return false;
			}
			EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, IntValue);
		}
		else
		{
			EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, FCString::Atoi64(*XmlValue));
		}
	}
	else if (UNumericProperty* NumericProperty = Cast<UNumericProperty>(Property))
	{
		if (NumericProperty->IsEnum() && !FCString::IsNumeric(*XmlValue))
		{
			const UEnum* Enum = NumericProperty->GetIntPropertyEnum();
			check(Enum);	// should be assured by IsEnum()
			int64 IntValue = Enum->GetValueByName(FName(*XmlValue));
			if (IntValue == INDEX_NONE)
			{
				UE_LOG(LogXml, Error, TEXT("XmlValueToUProperty - Unable import enum %s from string value %s for property %s"),
					*Enum->CppType, *XmlValue, *Property->GetNameCPP());
				return false;
			}
			NumericProperty->SetIntPropertyValue(OutValue, IntValue);
		}
		else if (NumericProperty->IsFloatingPoint())
		{
			NumericProperty->SetFloatingPointPropertyValue(OutValue, FCString::Atof(*XmlValue));
		}
		else if (NumericProperty->IsInteger())
		{
			NumericProperty->SetIntPropertyValue(OutValue, FCString::Atoi64(*XmlValue));
		}
		else
		{
			UE_LOG(LogXml, Error, TEXT("XmlValueToUProperty - Unable to set numeric property type %s for property %s"),
				*Property->GetClass()->GetName(), *Property->GetNameCPP());
			return false;
		}
	}
	else if (UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property))
	{
		BoolProperty->SetPropertyValue(OutValue, XmlValue.ToBool());
	}
	else if (UStrProperty* StringProperty = Cast<UStrProperty>(Property))
	{
		StringProperty->SetPropertyValue(OutValue, XmlValue);
	}
	else if (UTextProperty* TextProperty = Cast<UTextProperty>(Property))
	{
		TextProperty->SetPropertyValue(OutValue, FText::FromString(XmlValue));
	}
	else
	{
		if (Property->ImportText(*XmlValue, OutValue, 0, NULL) == NULL)
		{
			UE_LOG(LogXml, Error, TEXT("XmlValueToUProperty - Unable import property type %s from string value for property %s"),
				*Property->GetClass()->GetName(), *Property->GetNameCPP());
			return false;
		}
	}

	return true;
}

FString UXmlOperateLibrary::GetShortName(UProperty* Property)
{
	FString PropertyName = Property->GetName();
	FString Left;
	FString Right;
	if (PropertyName.Split("_", &Left, &Right))
	{
		return Left;
	}

	return PropertyName;
}

 

參考:

https://yinpengd.github.io/2019/06/30/UE4%E6%95%B0%E6%8D%AE%E8%A7%A3%E6%9E%90%E5%8F%8A%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章