繁体   English   中英

使用 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>

我使用这个类反序列化:

[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执行此操作,方法是使用单个集合捕获<a><b>元素并将[XmlElement(Name, Type = typeof(...))]属性多次应用到它,每个应用一次所需的元素名称。 因为您使用单个集合来反序列化这两个元素,所以会自动保留顺序。 但是,要实现此目的, XmlSerializer必须能够在重新序列化时确定正确的元素名称。 有两种方法可以实现这一点,如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]属性进行标识。

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

我发现选项 #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 小提琴

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

不,基本上; XmlSerializer不支持。 如果要使用该选项,则需要使用XDocumentXmlDocumentXmlReader手动编写它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM