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 元素的架构。选择元素表示一组固定元素中的一个(在您的情况下为&lt;Parameter1&gt;&lt;Parameter2&gt;)将出现在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 类型来反序列化&lt;Parameter1&gt;&lt;Parameter2&gt; 元素,则必须引入辅助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> 对象中的主要内容,如果未能解决你的问题,请参考以下文章

将 Web 服务调用的结果反序列化到子元素类型

Jackson xml反序列化 - 序列化为一个列表,其中包含任意元素

将子XML元素反序列化为XML字符串

当元素可能是许多可能元素之一时,将 XML 元素反序列化为对象

在C#中序列化和反序列化之间保留xml元素的顺序

在 C# 中反序列化 XML 元素的存在以布尔