使用 XmlSerializer 反序列化和序列化 XML 时保持排序

Keep sort when deserialize and serialize XML using XmlSerializer

我有以下测试XML字符串:

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

我使用这个反序列化 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; }
}

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

<?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.

相同

我想像那样在我的列表中添加订单 属性

[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; }
}

但不知道连载时如何按他们排序

基本上没有; XmlSerializer 不支持。如果您想使用该选项,则需要使用 XDocumentXmlDocumentXmlReader.

手动编写

您可以使用 XmlSerializer 通过使用单个集合来捕获 <a><b> 元素并应用 [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)] 构造函数将元素名称映射到具体项类型。例如,如果您有一个可能是字符串或整数的元素序列,如下所示:

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

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

    public class Things
    {
        [XmlElement(Type = typeof(string)),
        XmlElement(Type = typeof(int))]
        public List<object> StringsAndInts { get; set; }
    }
    
  2. 如果集合只包含单一类型的项目,元素名称可以编码在一个关联的 enum 值数组中,其中 enum 名称对应于元素名称和数组本身通过 [XmlChoiceIdentifierAttribute] 属性标识。

    详情见documentation examples

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

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# 对象的更多示例,请参见 here or