在 C# 中使用混合元素顺序反序列化/序列化 XML 时出现问题

问题描述 投票:0回答:1

我在将 XML 反序列化/序列化为 C# 对象时遇到问题。 XML 结构具有混合的元素顺序,并且我遇到了具有相同名称但不同用途的元素的问题。这是我的 C# 类的简化版本:

public class Uientry
{
    [XmlElement(ElementName = "u")]
    public string ID { get; set; }
    
    [XmlElement(ElementName = "s")]
    public List<string> Strings { get; set; }

    [XmlElement(ElementName = "i")]
    public List<string> Integer { get; set; }
    [XmlElement(ElementName = "additional_data")]
    public Additional_data Additional_data { get; set; }
}

当 XML 具有名称相同但用途不同的元素时,就会出现问题 并且它们的顺序不固定。例如,

<i>
元素(代表整数)可能出现在
<s>
元素(代表字符串)之前和之后,从而导致冲突。

重要:我希望序列化后元素的顺序保持不变

这是我的一些xml代码

  <uientry>
    <u>606917248</u><!-- ID (80 d2 2c 24) -->
    <s>root</s><!-- title -->
    <s>CampaignRoot</s><!-- title2 -->
    <i>0</i><!-- x offset -->
    <i>0</i><!-- y offset -->
    <no /><!-- uientry flag 1 -->
    <yes /><!-- uientry flag 2 -->
    <byte>1</byte><!-- uientry flag 3 -->
    <no /><!-- uientry flag 4 -->
    <no /><!-- uientry flag 5 -->
    <no /><!-- uientry flag 6 -->
    <no /><!-- uientry flag 7 -->
    <yes /><!-- uientry flag 8 -->
    <no /><!-- uientry flag 9 -->
    <no /><!-- uientry flag 10 -->
    <no /><!-- uientry flag 11 -->
    <no /><!-- uientry flag 12 -->
    <yes />
    <unicode></unicode><!-- tooltip text -->
    <unicode></unicode><!-- tooltip id -->
    <i>0</i><!-- 00:00:00:00 --><!-- docking? -->
    <i>0</i><!-- 00:00:00:00 --><!-- docking x? -->
    <i>0</i><!-- 00:00:00:00 --><!-- docking y? -->
    <no />
    <i>0</i><!-- default state id -->
    <images count="1">
      <image>
        <u>606535040</u><!-- ID (80 fd 26 24) -->
        <s></s><!-- path -->
        <i>1280</i><!-- x size -->
        <i>960</i><!-- y size -->
        <no />
      </image>
    </images>
    <i>0</i><!-- 00:00:00:00 --><!-- mask image? -->
    <i>0</i>
    <states count="0">
    </states>
    <children count="0">
    </children>
    <additional_data type="none" />
  </uientry>

如何修改我的 C# 类或使用属性来处理这种情况?有没有办法指示 XML 反序列化器考虑元素的用途并进行相应的处理?

感谢您的帮助:)

c# xml xml-serialization xml-deserialization
1个回答
0
投票

您的 XML 的架构包含一系列 choice 元素。选择元素指示一组固定元素(

<u>
<i>
<yes>
<no>
<unicode>
等)中的一个将出现在 XML 中。
XmlSerializer
支持选择元素,如选择元素绑定支持中所述:

如果各个选择元素的类型及其名称不同,Xsd.exe 仅将

XmlElementAttribute
属性应用于公共成员。如果它们仅在名称上有所不同,Xsd.exe 会另外应用
XmlChoiceIdentifierAttribute
,并添加额外的逻辑来进行选择。

因此,正如 Xml 反序列化 - 将两个元素合并到单个 List 对象中中所解释的,您可以将其建模为多态类型列表,也可以将其建模为一对选择类型和值的数组。我个人认为第一个选项更容易,所以让我们继续吧。 首先定义一些基本类型

UientryItem

,然后对于

<uientry>
中可能出现的每个元素,创建与该元素相对应的子类型,并通过
XmlTypeAttribute.TypeName
属性指示名称:
public abstract class UientryItem; [XmlType("u"), XmlRoot("u")] public class UientryId : UientryItem { [XmlText] public string Value { get; set; } } [XmlType("s"), XmlRoot("s")] public class UientryTitle : UientryItem { [XmlText] public string Value { get; set; } } [XmlType("no"), XmlRoot("no")] public class UientryNo : UientryItem; [XmlType("yes"), XmlRoot("yes")] public class UientryYes : UientryItem; [XmlType("byte"), XmlRoot("byte")] public class UientryByte : UientryItem { [XmlText] public byte Value { get; set; } } [XmlType("unicode"), XmlRoot("unicode")] public class UientryUnicode : UientryItem { [XmlText] public string Value { get; set; } } [XmlType("i"), XmlRoot("i")] public class UientryInt : UientryItem { [XmlText] public int Value { get; set; } } [XmlType("images"), XmlRoot("images")] public class UientryImages : UientryItem { [XmlAttribute("count")] public int Count { get => Images?.Count?? 0; set { } } [XmlElement("image")] public List<UientryImage> Images { get; set; } = new(); } [XmlType("image"), XmlRoot("image")] public class UientryImage { [XmlElement(typeof(UientryId)), XmlElement(typeof(UientryTitle)), XmlElement(typeof(UientryNo)), XmlElement(typeof(UientryYes)), XmlElement(typeof(UientryByte)), XmlElement(typeof(UientryUnicode)), XmlElement(typeof(UientryInt)), XmlElement(typeof(UientryImages)), XmlElement(typeof(UientryStates)), XmlElement(typeof(UientryChildren)), XmlElement(typeof(UientryAdditionalData))] public List<UientryItem> Items { get; set; } = new(); } [XmlType("states"), XmlRoot("states")] public class UientryStates : UientryItem { [XmlAttribute("count")] public int Count { get; set; } // TODO: fill in appropriate properties } [XmlType("children"), XmlRoot("children")] public class UientryChildren : UientryItem { [XmlAttribute("count")] public int Count { get; set; } // TODO: fill in appropriate properties } [XmlType("additional_data"), XmlRoot("additional_data")] public class UientryAdditionalData : UientryItem { [XmlAttribute("type")] public string Type { get; set; } // TODO: fill in appropriate properties }

接下来定义
Uientry

如下:

[XmlType("uientry"), XmlRoot("uientry")]
public class Uientry
{
    [XmlElement(typeof(UientryId)), 
     XmlElement(typeof(UientryTitle)),
     XmlElement(typeof(UientryNo)),
     XmlElement(typeof(UientryYes)),
     XmlElement(typeof(UientryByte)),
     XmlElement(typeof(UientryUnicode)),
     XmlElement(typeof(UientryInt)),
     XmlElement(typeof(UientryImages)),
     XmlElement(typeof(UientryStates)),
     XmlElement(typeof(UientryChildren)),
     XmlElement(typeof(UientryAdditionalData))]
    public List<UientryItem> Items { get; set; } = new();
}

您将能够使用 
<uientry>

反序列化您的

new XmlSerializer(typeof(Uientry))
,例如如下:
using var textReader = new StringReader(xmlString);
var serializer = new XmlSerializer(typeof(Uientry));

var uiEntry = (Uientry)serializer.Deserialize(textReader);

演示小提琴
在这里

© www.soinside.com 2019 - 2024. All rights reserved.