You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中XML混合元素顺序的序列化与反序列化问题处理

解决方案:处理XML元素顺序不固定+同名多用途的序列化/反序列化问题

你的核心问题在于:XmlSerializer默认会把所有同名元素(比如<i>)归集到同一个集合中,无法区分不同位置的同名元素用途,同时还需要严格保留元素的原始顺序。要解决这个问题,你需要用混合元素列表+类型标识的方式建模,配合XmlSerializer的特性实现精准映射。

步骤1:定义元素类型枚举

先枚举所有可能出现在<uientry>下的元素类型,覆盖XML里的所有标签:

public enum UientryElementType
{
    u,
    s,
    i,
    no,
    yes,
    byte,
    unicode,
    images,
    states,
    children,
    additional_data
}

步骤2:创建可变元素类

这个类封装每个XML元素的类型、值(或子对象),通过特性告诉XmlSerializer如何映射:

public class UientryElement
{
    [XmlIgnore]
    public UientryElementType ElementType { get; set; }

    [XmlElement(ElementName = "u")]
    public string UValue
    {
        get => ElementType == UientryElementType.u ? Value : null;
        set { Value = value; ElementType = UientryElementType.u; }
    }

    [XmlElement(ElementName = "s")]
    public string SValue
    {
        get => ElementType == UientryElementType.s ? Value : null;
        set { Value = value; ElementType = UientryElementType.s; }
    }

    [XmlElement(ElementName = "i")]
    public string IValue
    {
        get => ElementType == UientryElementType.i ? Value : null;
        set { Value = value; ElementType = UientryElementType.i; }
    }

    [XmlElement(ElementName = "byte")]
    public string ByteValue
    {
        get => ElementType == UientryElementType.byte ? Value : null;
        set { Value = value; ElementType = UientryElementType.byte; }
    }

    [XmlElement(ElementName = "unicode")]
    public string UnicodeValue
    {
        get => ElementType == UientryElementType.unicode ? Value : null;
        set { Value = value; ElementType = UientryElementType.unicode; }
    }

    // 处理空元素<no>/<yes>
    [XmlElement(ElementName = "no")]
    public object NoElement
    {
        get => ElementType == UientryElementType.no ? new object() : null;
        set { ElementType = UientryElementType.no; }
    }

    [XmlElement(ElementName = "yes")]
    public object YesElement
    {
        get => ElementType == UientryElementType.yes ? new object() : null;
        set { ElementType = UientryElementType.yes; }
    }

    // 处理复杂子元素
    [XmlElement(ElementName = "images")]
    public Images ImagesElement
    {
        get => ElementType == UientryElementType.images ? ImagesValue : null;
        set { ImagesValue = value; ElementType = UientryElementType.images; }
    }

    [XmlElement(ElementName = "states")]
    public States StatesElement
    {
        get => ElementType == UientryElementType.states ? StatesValue : null;
        set { StatesValue = value; ElementType = UientryElementType.states; }
    }

    [XmlElement(ElementName = "children")]
    public Children ChildrenElement
    {
        get => ElementType == UientryElementType.children ? ChildrenValue : null;
        set { ChildrenValue = value; ElementType = UientryElementType.children; }
    }

    [XmlElement(ElementName = "additional_data")]
    public Additional_data AdditionalDataElement
    {
        get => ElementType == UientryElementType.additional_data ? AdditionalDataValue : null;
        set { AdditionalDataValue = value; ElementType = UientryElementType.additional_data; }
    }

    // 内部存储简单元素的值
    [XmlIgnore]
    public string Value { get; set; }

    [XmlIgnore]
    public Images ImagesValue { get; set; }

    [XmlIgnore]
    public States StatesValue { get; set; }

    [XmlIgnore]
    public Children ChildrenValue { get; set; }

    [XmlIgnore]
    public Additional_data AdditionalDataValue { get; set; }
}

// 补充XML中需要的子元素类
public class Images
{
    [XmlAttribute("count")]
    public int Count { get; set; }

    [XmlElement("image")]
    public List<Image> ImageList { get; set; }
}

public class Image
{
    [XmlElement("u")]
    public string U { get; set; }

    [XmlElement("s")]
    public string S { get; set; }

    [XmlElement("i")]
    public List<string> IList { get; set; }

    [XmlElement("no")]
    public object No { get; set; }
}

public class States
{
    [XmlAttribute("count")]
    public int Count { get; set; }
}

public class Children
{
    [XmlAttribute("count")]
    public int Count { get; set; }
}

public class Additional_data
{
    [XmlAttribute("type")]
    public string Type { get; set; }
}

步骤3:修改Uientry类

用混合元素列表替代原来的单独集合,添加XmlChoiceIdentifier特性关联元素类型:

public class Uientry
{
    [XmlChoiceIdentifier("ElementTypes")]
    [XmlElement("u", Type = typeof(UientryElement))]
    [XmlElement("s", Type = typeof(UientryElement))]
    [XmlElement("i", Type = typeof(UientryElement))]
    [XmlElement("no", Type = typeof(UientryElement))]
    [XmlElement("yes", Type = typeof(UientryElement))]
    [XmlElement("byte", Type = typeof(UientryElement))]
    [XmlElement("unicode", Type = typeof(UientryElement))]
    [XmlElement("images", Type = typeof(UientryElement))]
    [XmlElement("states", Type = typeof(UientryElement))]
    [XmlElement("children", Type = typeof(UientryElement))]
    [XmlElement("additional_data", Type = typeof(UientryElement))]
    public List<UientryElement> Elements { get; set; }

    [XmlIgnore]
    public List<UientryElementType> ElementTypes
    {
        get => Elements?.Select(e => e.ElementType).ToList();
        set { /* 仅用于XmlSerializer识别类型,无需赋值 */ }
    }
}

步骤4:序列化/反序列化示例

反序列化时,每个XML元素会按顺序封装到Elements列表中,你可以通过ElementType和位置区分用途;序列化时严格按照列表顺序输出元素,完全保留原始顺序:

// 反序列化示例
var serializer = new XmlSerializer(typeof(Uientry));
using var reader = new StringReader(xmlContent);
var uientry = (Uientry)serializer.Deserialize(reader);

// 遍历元素,区分不同用途的<i>
foreach (var elem in uientry.Elements)
{
    switch (elem.ElementType)
    {
        case UientryElementType.i:
            var index = uientry.Elements.IndexOf(elem);
            if (index == 3) Console.WriteLine($"X Offset: {elem.IValue}");
            else if (index == 4) Console.WriteLine($"Y Offset: {elem.IValue}");
            // 其他位置的<i>按需处理
            break;
        case UientryElementType.s:
            Console.WriteLine($"String value: {elem.SValue}");
            break;
        // 其他类型处理...
    }
}

// 序列化示例
using var writer = new StringWriter();
serializer.Serialize(writer, uientry);
var outputXml = writer.ToString();

额外说明

如果需要将特定位置的同名元素直接映射到类的属性(比如把前两个<i>映射到XOffset和YOffset),可以在Uientry类中添加计算属性,自行处理列表与属性的同步;或者实现IXmlSerializable接口自定义序列化逻辑,但混合列表方式更灵活,更适配元素顺序不固定的场景。

内容的提问来源于stack exchange,提问作者Nick G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 11:22:12