在将XML反序列化/序列化为C#对象时,我遇到了一个问题.XML struct 具有混合的元素顺序,我遇到了名称相同但用途不同的元素的问题.下面是我的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具有具有相同名称但不同目的的元素时就会出现问题,and their order is not fixed. For example, the 100 elements (representing integers) can appear before and after 101 elements (representing strings), causing conflicts.

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

this is some of my xml code

<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反序列化程序考虑元素的用途并相应地处理它?

推荐答案

您的XML具有一个包含choice个元素的序列的架构.CHOICE元素表示一组固定的元素之一--<u><i><yes><no><unicode>等等--将出现在XML中.XmlSerializer支持Choice Element Binding Support中所述的 Select 元素:

如果单个CHOICE元素的类型与其名称不同,则Xsd.exe仅将XmlElementAttribute个属性应用于公共成员.如果它们只是在名称上不同,则Xsd.exe会应用XmlChoiceIdentifierAttribute,并为做出 Select 添加额外的逻辑.

因此,如第this answer101节所述,您可以将其建模为一组多态类型,也可以将其建模为一对 Select 类型和值的array.就我个人而言,我认为第一个 Select 更容易,所以我们就这么做吧.

首先定义一些基本类型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();
}

您将能够使用new XmlSerializer(typeof(Uientry))对您的<uientry>进行反序列化,例如:

using var textReader = new StringReader(xmlString);
var serializer = new XmlSerializer(typeof(Uientry));

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

并且元素顺序将通过public List<UientryItem> Items { get; set; }列表中的项的顺序来保留.

备注:

  • 如果您确定某些元素出现在<uientry>元素的开头或结尾--例如,<additional_data>总是出现在最后--那么您可以使用一个单独的属性来捕获它,就像您在原始的Uientry类中所做的那样.

演示小提琴here.

Csharp相关问答推荐

如何将ref T*重新解释为ref nint?

如何使用FastEndpoints和.NET 8 WebAppliationBuilder进行集成测试?

无法将blob发送到Azure -缺少HTTP标头异常

无法使用ternal- .net修复可空警告

如何循环遍历XML文档 node 以使用XSLT存储值

如何在C#中删除一个特殊字符,如"使用Regex"

是否可以使用EF—Core进行临时部分更新?

实现List T,为什么LINQ之后它不会返回MyList?<>(无法强制转换WhereListIterator `1类型的对象)'

MAUI查询参数单一字符串项将不起作用

使用Orleans进行的单元测试找不到接口的实现

如何将MongoDB序列化程序设置为内部对象属性

将类移动到新命名空间后更新RavenDB Raven-Clr-Type

正确处理嵌套的本机集合

EFR32BG22 BLE在SPP模式下与PC(Windows 10)不连接

Polly重试URL复制值

当try 测试具有协变返回类型的抽象属性时,类似功能引发System.ArgumentException

序列化过程中的死循环

将字符串类型日期输入(yyyy-mm-ddthh:mm:ss)转换为MM/dd/yyyy格式

避免在特定区域中设置Visual Studio代码的自动格式

与另一个对象位于同一位置的对象具有不同的变换位置