Files
2025-05-18 13:04:45 +08:00

245 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Backends/XmlStructSerializerBackend.h"
#include "XmlSerializationModule.h"
#include "UObject/EnumProperty.h"
#include "UObject/PropertyPortFlags.h"
#include "UObject/TextProperty.h"
#include "UObject/UnrealType.h"
#include "Utils/XmlUtils.h"
#include "Utils/XmlWriter.h"
#include "pugixml.hpp"
class FXmlStructSerializerBackend::FImpl
{
public:
FImpl()
{
pugi::xml_node decl = XmlDoc.prepend_child(pugi::node_declaration);
decl.append_attribute("version") = "1.0";
decl.append_attribute("encoding") = "utf-8";
XmlElement = XmlDoc.append_child();
XmlElement.set_name("StructFileDocument");
XmlElement.append_attribute(("Version")) = "1.0";
}
pugi::xml_document XmlDoc;
pugi::xml_node XmlElement;
TUniquePtr<FXmlWriter> XmlWriter;
};
FXmlStructSerializerBackend::FXmlStructSerializerBackend(FArchive& InArchive,
const EStructSerializerBackendFlags InFlags)
: Impl(new FImpl)
, Flags(InFlags)
{
Impl->XmlWriter = MakeUnique<FXmlWriter>(&InArchive);
}
FXmlStructSerializerBackend::~FXmlStructSerializerBackend()
{
delete Impl;
}
void FXmlStructSerializerBackend::SaveDocument(EXmlSerializationEncoding InEncoding)
{
using namespace UE::XmlSerialization::Private;
const pugi::xml_encoding encoding = Utils::ToPugiEncoding(InEncoding);
Impl->XmlDoc.save(*Impl->XmlWriter, " ", pugi::format_default, encoding);
}
void FXmlStructSerializerBackend::BeginArray(const FStructSerializerState& InState)
{
pugi::xml_node ArrayElement = Impl->XmlElement.append_child(pugi::node_element);
ArrayElement.set_name("Array");
if (InState.ValueProperty)
{
ArrayElement.append_attribute("Name") = STRING_CAST_UE_TO_PUGI(*InState.ValueProperty->GetName());
ArrayElement.append_attribute("Type") = STRING_CAST_UE_TO_PUGI(*InState.ValueProperty->GetClass()->GetName());
}
if (InState.ValueType)
{
ArrayElement.append_attribute("ValueType") = STRING_CAST_UE_TO_PUGI(*InState.ValueType->GetName());
}
Impl->XmlElement = ArrayElement;
}
void FXmlStructSerializerBackend::BeginStructure(const FStructSerializerState& InState)
{
pugi::xml_node StructElement = Impl->XmlElement.append_child(pugi::node_element);
StructElement.set_name("Struct");
if (InState.ValueProperty)
{
StructElement.append_attribute("Name") = STRING_CAST_UE_TO_PUGI(*InState.ValueProperty->GetName());
StructElement.append_attribute("Type") = STRING_CAST_UE_TO_PUGI(*InState.ValueProperty->GetClass()->GetName());
}
if (InState.ValueType)
{
StructElement.append_attribute("ValueType") = STRING_CAST_UE_TO_PUGI(*InState.ValueType->GetName());
}
Impl->XmlElement = StructElement;
}
void FXmlStructSerializerBackend::EndArray(const FStructSerializerState& /*State*/)
{
// Check that current element is an Array?
Impl->XmlElement = Impl->XmlElement.parent();
}
void FXmlStructSerializerBackend::EndStructure(const FStructSerializerState& /*State*/)
{
// Check that current element is a Struct?
Impl->XmlElement = Impl->XmlElement.parent();
}
void FXmlStructSerializerBackend::WriteComment(const FString& InComment)
{
pugi::xml_node Child = Impl->XmlElement.append_child(pugi::node_comment);
Child.set_value(STRING_CAST_UE_TO_PUGI(*InComment));
}
void FXmlStructSerializerBackend::WriteProperty(const FStructSerializerState& InState, int32 InArrayIndex)
{
// Append a property element in the current structure element.
pugi::xml_node PropertyElement = Impl->XmlElement.append_child(pugi::node_element);
PropertyElement.set_name("Property");
PropertyElement.append_attribute("Name") = STRING_CAST_UE_TO_PUGI(*InState.ValueProperty->GetName());
PropertyElement.append_attribute("Type") = STRING_CAST_UE_TO_PUGI(*InState.ValueProperty->GetClass()->GetName());
if (InState.ValueType)
{
PropertyElement.append_attribute("ValueType") = STRING_CAST_UE_TO_PUGI(*InState.ValueType->GetName());
}
pugi::xml_attribute ValueAttribute = PropertyElement.append_attribute("Value");
// booleans
if (InState.FieldType == FBoolProperty::StaticClass())
{
ValueAttribute.set_value(CastFieldChecked<FBoolProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
// unsigned bytes & enumerations
else if (InState.FieldType == FEnumProperty::StaticClass())
{
FEnumProperty* EnumProperty = CastFieldChecked<FEnumProperty>(InState.ValueProperty);
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*EnumProperty->GetEnum()->GetNameStringByValue(EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(EnumProperty->ContainerPtrToValuePtr<void>(InState.ValueData, InArrayIndex)))));
}
else if (InState.FieldType == FByteProperty::StaticClass())
{
FByteProperty* ByteProperty = CastFieldChecked<FByteProperty>(InState.ValueProperty);
if (ByteProperty->IsEnum())
{
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*ByteProperty->Enum->GetNameStringByValue(ByteProperty->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex))));
}
else
{
ValueAttribute.set_value((int)ByteProperty->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
}
// floating point numbers
else if (InState.FieldType == FDoubleProperty::StaticClass())
{
ValueAttribute.set_value(CastFieldChecked<FDoubleProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
else if (InState.FieldType == FFloatProperty::StaticClass())
{
ValueAttribute.set_value(CastFieldChecked<FFloatProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
// signed integers
else if (InState.FieldType == FIntProperty::StaticClass())
{
ValueAttribute.set_value((int)CastFieldChecked<FIntProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
else if (InState.FieldType == FInt8Property::StaticClass())
{
ValueAttribute.set_value((int)CastFieldChecked<FInt8Property>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
else if (InState.FieldType == FInt16Property::StaticClass())
{
ValueAttribute.set_value((int)CastFieldChecked<FInt16Property>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
else if (InState.FieldType == FInt64Property::StaticClass())
{
ValueAttribute.set_value((long long)CastFieldChecked<FInt64Property>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
// unsigned integers
else if (InState.FieldType == FUInt16Property::StaticClass())
{
ValueAttribute.set_value((unsigned int)CastFieldChecked<FUInt16Property>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
else if (InState.FieldType == FUInt32Property::StaticClass())
{
ValueAttribute.set_value((unsigned int)CastFieldChecked<FUInt32Property>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
else if (InState.FieldType == FUInt64Property::StaticClass())
{
ValueAttribute.set_value((unsigned long long)CastFieldChecked<FUInt64Property>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex));
}
// names, strings & text
else if (InState.FieldType == FNameProperty::StaticClass())
{
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*CastFieldChecked<FNameProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex).ToString()));
}
else if (InState.FieldType == FStrProperty::StaticClass())
{
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*CastFieldChecked<FStrProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex)));
}
else if (InState.FieldType == FTextProperty::StaticClass())
{
const FText& TextValue = CastFieldChecked<FTextProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex);
if (EnumHasAnyFlags(Flags, EStructSerializerBackendFlags::WriteTextAsComplexString))
{
FString TextValueString;
FTextStringHelper::WriteToBuffer(TextValueString, TextValue);
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*TextValueString));
}
else
{
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*TextValue.ToString()));
}
}
// classes & objects
else if (InState.FieldType == FSoftClassProperty::StaticClass())
{
FSoftObjectPtr const& Value = CastFieldChecked<FSoftClassProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex);
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*(Value.IsValid() ? Value->GetPathName() : FString())));
}
else if (InState.FieldType == FWeakObjectProperty::StaticClass())
{
FWeakObjectPtr const& Value = CastFieldChecked<FWeakObjectProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex);
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*(Value.IsValid() ? Value.Get()->GetPathName() : FString())));
}
else if (InState.FieldType == FSoftObjectProperty::StaticClass())
{
FSoftObjectPtr const& Value = CastFieldChecked<FSoftObjectProperty>(InState.ValueProperty)->GetPropertyValue_InContainer(InState.ValueData, InArrayIndex);
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*Value.ToString()));
}
else if (FObjectProperty* ObjectProperty = CastField<FObjectProperty>(InState.ValueProperty))
{
// @TODO: Could this be expanded to include everything derived from FObjectPropertyBase?
// Generic handling for a property type derived from FObjectProperty that is obtainable as a pointer and will be stored using its path.
// This must come after all the more specialized handlers for object property types.
UObject* const Value = ObjectProperty->GetObjectPropertyValue_InContainer(InState.ValueData, InArrayIndex);
ValueAttribute.set_value(STRING_CAST_UE_TO_PUGI(*(Value ? Value->GetPathName() : FString())));
}
// unsupported property type
else
{
const FString ValueType = InState.ValueType != nullptr ? InState.ValueType->GetFName().ToString() : TEXT("unknown");
UE_LOG(LogXmlSerialization, Verbose, TEXT("FXmlStructSerializerBackend: Property %s cannot be serialized, because its type (%s) is not supported")
, *InState.ValueProperty->GetFName().ToString()
, *ValueType);
}
}