使用 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
不支持。如果您想使用该选项,则需要使用 XDocument
或 XmlDocument
或 XmlReader
.
手动编写
您可以使用 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:
如果集合包含多态项,可以使用 [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; }
}
如果集合只包含单一类型的项目,元素名称可以编码在一个关联的 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 fiddle。
有关使用 [XmlChoiceIdentifier]
将具有不同名称的元素序列反序列化为相同类型的 c# 对象的更多示例,请参见 here or 。
我有以下测试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
不支持。如果您想使用该选项,则需要使用 XDocument
或 XmlDocument
或 XmlReader
.
您可以使用 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:
如果集合包含多态项,可以使用
[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; } }
如果集合只包含单一类型的项目,元素名称可以编码在一个关联的
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 fiddle。
有关使用 [XmlChoiceIdentifier]
将具有不同名称的元素序列反序列化为相同类型的 c# 对象的更多示例,请参见 here or