使用了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;
}
參考: