使用 XmlSerializer 反序列化和序列化 XML 时保持排序 [英] Keep sort when deserialize and serialize XML using XmlSerializer

查看:44
本文介绍了使用 XmlSerializer 反序列化和序列化 XML 时保持排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下测试 XML 字符串:

I have the following test XML string:

<?xml version="1.0" encoding="UTF-8"?>
<test id="myid">
 <b>b1</b>
 <a>a2</a>
 <a>a1</a>
 <b>b2</b>
</test>

我使用这个类反序列化:

which I deserialize using this class:

[XmlRoot(ElementName = "test")]
public class Test
{
    [XmlElement(ElementName = "a")]
    public List<string> A { get; set; }
    [XmlElement(ElementName = "b")]
    public List<string> B { get; set; }
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }
}

如果我现在要序列化对象,结果将是:

If I'm now going to serialize the object the result will be:

<?xml version="1.0" encoding="utf-16"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="myid">
  <a>a2</a>
  <a>a1</a>
  <b>b1</b>
  <b>b2</b>
</test>

有没有办法保持最初的排序顺序?我想我不能使用 [XmlElementAttribute(Order = x)] 因为订单不应该被硬编码,而是与初始 xml 相同.

Is there a way to keep the initial sort order? I guess I can't use [XmlElementAttribute(Order = x)] cause the order shouldn't be hardcoded but identically with the initial xml.

我想过在我的列表中添加一个订单属性

I thought about adding an order property to my lists like that

[XmlRoot(ElementName="a")]
public class A 
{
    [XmlAttribute(AttributeName="order")]
    public string Order { get; set; }
    [XmlText]
    public string Text { get; set; }
}

[XmlRoot(ElementName="b")]
public class B 
{
    [XmlAttribute(AttributeName="order")]
    public string Order { get; set; }
    [XmlText]
    public string Text { get; set; }
}

[XmlRoot(ElementName="test")]
public class Test 
{
    [XmlElement(ElementName="a")]
    public List<A> A { get; set; }
    [XmlElement(ElementName="b")]
    public List<B> B { get; set; }
    [XmlAttribute(AttributeName="id")]
    public string Id { get; set; }
}

但我不知道如何在序列化时按它们排序.

but I don't know how to order by them when serializing.

推荐答案

您可以使用 XmlSerializer 做到这一点,通过使用单个集合来捕获 <b> 元素并应用 [XmlElement(Name, Type = typeof(...))] 多次赋予它属性,每个所需的元素名称一次.因为您使用单个集合来反序列化这两个元素,所以会自动保留顺序.但是,要使这项工作起作用,XmlSerializer 必须能够在重新序列化时确定正确的元素名称.有两种方法可以实现这一点,如选择元素绑定支持:

You can do this with XmlSerializer by using a single collection to capture both the <a> and <b> elements and applying the [XmlElement(Name, Type = typeof(...))] attribute to it multiple times, once for each desired element name. Because you are using a single collection to deserialize both elements, the order is automatically preserved. However, to make this work, XmlSerializer must be able to determine the correct element name when re-serializing. There are two approaches to accomplish this, as documented in Choice Element Binding Support:

  1. 如果集合包含多态项,则可以使用 [XmlElementAttribute(String, Type)] 构造函数.例如,如果您有一个元素序列,这些元素可能是字符串或整数,如下所示:

  1. If the collection contains polymorphic items, the element name can be mapped to the concrete item type by using the [XmlElementAttribute(String, Type)] constructor. For instance, if you have a sequence of elements that might be strings or integers like so:

<Things>
   <string>Hello</string>
   <int>999</int>
</Things>

这可以绑定到一个集合,如下所示:

This can be bound to a collection as follows:

public class Things
{
    [XmlElement(Type = typeof(string)),
    XmlElement(Type = typeof(int))]
    public List<object> StringsAndInts { get; set; }
}

  • 如果集合只包含单一类型的项目,元素名称可以编码在一个关联的 enum 值数组中,其中 enum 名称对应元素名称和数组本身通过 [XmlChoiceIdentifierAttribute] 属性.

  • If the collection contains only a single type of item, the element name can be encoded in an associated array of enum values, where the enum names correspond to the element names and the array itself is identified via the [XmlChoiceIdentifierAttribute] attribute.

    有关详细信息,请参阅文档示例.

    For details, see the documentation examples.

    我发现选项 #1 比选项 #2 更容易使用.使用这种方法,以下模型将反序列化和重新序列化您的 XML,同时成功地保留 元素的顺序:

    I find option #1 easier to work with than option #2. Using this approach, the following model will deserialize and re-serialize your XML while successfully preserving the order of the <a> and <b> elements:

    public abstract class StringElementBase
    {
        [XmlText]
        public string Text { get; set; }
    
        public static implicit operator string(StringElementBase element)
        {
            return element == null ? null : element.Text;
        }
    }
    
    public sealed class A : StringElementBase
    {
    }
    
    public sealed class B : StringElementBase
    {
    }
    
    [XmlRoot(ElementName = "test")]
    public class Test
    {
        [XmlElement("a", Type = typeof(A))]
        [XmlElement("b", Type = typeof(B))]
        public List<StringElementBase> Items { get; } = new List<StringElementBase>();
    
        [XmlIgnore]
        // For convenience, enumerate through the string values of the items.
        public IEnumerable<string> ItemValues { get { return Items.Select(i => (string)i); } }
    
        [XmlAttribute(AttributeName = "id")]
        public string Id { get; set; }
    }
    

    工作 .Net fiddle.

    有关使用 [XmlChoiceIdentifier] 将一系列具有不同名称的元素反序列化为相同类型的 c# 对象的更多示例,请参见例如 此处此处.

    For more examples of using [XmlChoiceIdentifier] to deserialize a sequence of elements with different names to c# objects of the same type, see for instance here or here.

    这篇关于使用 XmlSerializer 反序列化和序列化 XML 时保持排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆