Xml 反序列化 - 将两个元素合并到一个 List<T> 对象中
Posted
技术标签:
【中文标题】Xml 反序列化 - 将两个元素合并到一个 List<T> 对象中【英文标题】:Xml Deserialization - Merging two elements into a single List<T> object 【发布时间】:2016-09-15 09:19:03 【问题描述】:我有一个 XML 文档,并且使用反序列化,有没有办法将两个元素组合成一个对象?
XML 示例:
<Parameter1>3</Parameter1>
<Parameter2>4</Parameter2>
我想创建一个包含项目 3 和 4 的列表(参数类型)。
我尝试过使用 XmlArrayItem,例如:
[XmlArrayItem("Parameter1")]
[XmlArrayItem("Parameter2")]
[XmlArray]
public Parameter[] Parameters; // have also tried this as public List<Parameter> Parameters = new List<Parameter>();
我尝试过使用 XmlElements(但我不知道如何组合它们):
[XmlElement("Parameter1")]
public List<Parameter> Parameters = new List<Parameter>();
有没有什么方法可以做到这一点,而不仅仅是创建两个单独的列表并在以后组合它们?
请注意,不能更改 XML 格式。
【问题讨论】:
序列化要做什么? 另外,你需要知道给定元素序列化的元素名称吗? 要么我不明白这个问题,要么我不明白它是如何相关的,但我正在反序列化文件以向用户显示其中列出的选项。最终目标只是元素内的文本,不依赖于元素名称。 (当然,不同的元素适用于不同的选项,什么不是)。 【参考方案1】:您的 XML 有一个包含 choice 元素的架构。选择元素表示一组固定元素中的一个(在您的情况下为<Parameter1>
和<Parameter2>
)将出现在XML 中。 XmlSerializer
支持选择元素,如 Choice Element Binding Support 中所述:
如果单个选择元素的类型及其名称不同,Xsd.exe 仅将
XmlElementAttribute
属性应用于公共成员。如果它们仅在名称上有所不同,则 Xsd.exe 会另外应用XmlChoiceIdentifierAttribute
,并添加额外的逻辑来进行选择。
因此,您可以使用以下选项来反序列化您的 XML:
子类化您的Parameter
类并使用[XmlElementAttribute(String, Type)]
为每个元素名称指定不同的类型。实例化的特定Parameter
子类将因此捕获XML 元素名称。
即你可以这样做:
public abstract class Parameter
[XmlText]
public string Value get; set; // Could be int if you prefer.
public class Parameter1 : Parameter
public class Parameter2 : Parameter
[XmlType("Root")]
public class RootObject
[XmlElement("Parameter1", typeof(Parameter1))]
[XmlElement("Parameter2", typeof(Parameter2))]
public Parameter[] Parameters get; set;
如果您想使用相同的Parameter
类型来反序列化<Parameter1>
和<Parameter2>
元素,则必须引入辅助XmlChoiceIdentifierAttribute
数组来捕获XML 元素名称:
public class Parameter
[XmlText]
public string Value get; set;
[XmlType("Root")]
public class RootObject
[XmlElement("Parameter1", typeof(Parameter))]
[XmlElement("Parameter2", typeof(Parameter))]
[XmlChoiceIdentifier("ParametersElementName")]
public Parameter[] Parameters get; set;
[XmlIgnore]
public ParametersChoiceType[] ParametersElementName get; set;
[XmlType(IncludeInSchema = false)]
public enum ParametersChoiceType
Parameter1,
Parameter2,
反序列化后,ParametersElementName
数组将拥有与Parameters
数组相同的条目数,其中的enum
值将指示每个参数实际遇到的 XML 元素名称。
作为选项 2 的变体,如果您不需要捕获 XML 元素名称而只想反序列化值,则可以创建一个“假”选择数组属性,如下所示:
[XmlType("Root")]
public class RootObject
[XmlElement("Parameter1", typeof(Parameter))]
[XmlElement("Parameter2", typeof(Parameter))]
[XmlChoiceIdentifier("ParametersElementName")]
public Parameter[] Parameters get; set;
[XmlIgnore]
public ParametersChoiceType[] ParametersElementName
get
if (Parameters == null)
return null;
return Parameters.Select(p => ParametersChoiceType.Parameter1).ToArray();// Arbitrarily return ItemsChoiceType.Parameter1
set
// Do nothing - don't care.
XmlSerializer
要求您使用这两个选项之一。如果它无法通过类型或项目选择标识符确定正确的元素名称,它将抛出 InvalidOperationException
和消息:
You need to add XmlChoiceIdentifierAttribute to the 'Parameters' member.
原型fiddle 显示每个选项。
【讨论】:
【参考方案2】:如果你这样做会怎样:
//get the xml doc
const string str = @"<root>
<Parameter1>3</Parameter1>
<Parameter2>4</Parameter2>
</root>";
var xml = new XmlDocument();
//load it
xml.LoadXml(str);
//get the nodes where the names contain the string parameter
var xnList = xml.SelectNodes("//*[contains(name(),'Parameter')]");
//create a list of parameters
var list = new List<Parameter>();
//populate the list with the value in the node's innertext
foreach (XmlNode xn in xnList)
list.Add(new Parameter Value = int.Parse(xn.InnerText) );
foreach(var param in list)
Console.WriteLine(param.Value); //should print 3 and 4
我以这个类为例:
class Parameter
public int Value get; set;
【讨论】:
我使用的 XML 文档比我为示例提取的要大得多。另外,我正在使用 XmlSerializer(不是 XmlDocument),所以它不起作用。以上是关于Xml 反序列化 - 将两个元素合并到一个 List<T> 对象中的主要内容,如果未能解决你的问题,请参考以下文章
Jackson xml反序列化 - 序列化为一个列表,其中包含任意元素