如何在c#中使用具有相同名称但不同属性和结构的元素反序列化XML

Posted

技术标签:

【中文标题】如何在c#中使用具有相同名称但不同属性和结构的元素反序列化XML【英文标题】:How to deserialize XML with elements with same name, but different attributes and structure in c# 【发布时间】:2020-10-25 13:31:31 【问题描述】:

我有一个具有以下结构的 XML 响应:

 <Response>
    <Block id="1">
       <Some_Data_1><Some_Data_1>
       <Some_Data_2><Some_Data_2>
    </Block>
    <Block id="2">
        <Another_Data_3><Another_Data_3>
        <Another_Data_4><Another_Data_4>
        <Another_Data_5><Another_Data_5>
    </Block>
    <Block id="3">
        ...
    </Block>
</Response>

我需要以最快的方式将每个 Block 反序列化为单独的对象。 到目前为止,我想出了使用 LINQ 手动搜索每个块,并分别反序列化每个块,如下所示:

var Xblock1 = root.Elements("Block").FirstOrDefault(e => e.Attribute("id")?.Value == "1");
Block1 block1 = (Block1)(serializer.Deserialize(Xblock1.CreateReader()));

但我相信有更优化的方法来做到这一点。

珍惜你的时间

【问题讨论】:

【参考方案1】:

Xml 序列化很慢,需要类和属性名。相反,我推荐 Xml Linq,它更快并且不需要预定义的属性名称

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1

    class Program
    
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        
            XDocument doc = XDocument.Load(FILENAME);

            Dictionary<int, Dictionary<string, string>> dict = doc.Descendants("Block")
                .GroupBy(x => (int)x.Attribute("id"), y => y.Elements()
                    .GroupBy(a => a.Name.LocalName, b => (string)b)
                    .ToDictionary(a => a.Key, b => b.FirstOrDefault()))
                .ToDictionary(x => x.Key, y => y.FirstOrDefault());

        
    

【讨论】:

谢谢,但是如果我最终需要它作为对象呢?而且我知道所有的属性名称,这不是问题 你要求快。创建具有对象名称(或匿名)的类并使用比序列化更快的 xml linx。【参考方案2】:

第一种方法是使用序列化,第二种方法是从@jdweng 的答案中复制。

如前所述,使用Serialize的方法要慢得多!

对于第一种方法,您还需要一个由我在此答案末尾链接的 XSD.EXE 生成的类。

static void Main(string[] args)

    DateTime start;

    start = DateTime.Now;
    XmlSerializer ser2 = new XmlSerializer(typeof(Response));
    FileStream f = new FileStream(FILENAME, FileMode.Open);
    Response response = ser2.Deserialize(f) as Response;

    foreach (var item in response.Block)
    
        Console.WriteLine(item.id);
    
    f.Close();

    Console.WriteLine("Method 1: 0", ((DateTime.Now - start).TotalMilliseconds / 1000));

    start = DateTime.Now;
    XDocument doc = XDocument.Load(FILENAME);

    Dictionary<int, Dictionary<string, string>> dict = doc.Descendants("Block")
        .GroupBy(x => (int)x.Attribute("id"), y => y.Elements()
            .GroupBy(a => a.Name.LocalName, b => (string)b)
            .ToDictionary(a => a.Key, b => b.FirstOrDefault()))
        .ToDictionary(x => x.Key, y => y.FirstOrDefault());

    foreach (var item in dict.Keys)
    
        Console.WriteLine(item);
    
    Console.WriteLine("Method 2: 0", ((DateTime.Now - start).TotalMilliseconds / 1000));

    Console.ReadLine();


输出:

1
2
3
Method 1: 0,0759927
1
2
3
Method 2: 0,0030262

下面的代码是由XSD.EXE /c file.xsd 生成的,所以你应该从你的 XML 中创建一个 XSD(Visual Studio 可以为你做):

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System.Xml.Serialization;

// 
// This source code was auto-generated by xsd, Version=4.8.3928.0.
// 


/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class Response 
    
    private ResponseBlock[] blockField;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Block")]
    public ResponseBlock[] Block 
        get 
            return this.blockField;
        
        set 
            this.blockField = value;
        
    


/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class ResponseBlock 
    
    private string another_Data_3Field;
    
    private string another_Data_4Field;
    
    private string another_Data_5Field;
    
    private string some_Data_1Field;
    
    private string some_Data_2Field;
    
    private string[] textField;
    
    private byte idField;
    
    /// <remarks/>
    public string Another_Data_3 
        get 
            return this.another_Data_3Field;
        
        set 
            this.another_Data_3Field = value;
        
    
    
    /// <remarks/>
    public string Another_Data_4 
        get 
            return this.another_Data_4Field;
        
        set 
            this.another_Data_4Field = value;
        
    
    
    /// <remarks/>
    public string Another_Data_5 
        get 
            return this.another_Data_5Field;
        
        set 
            this.another_Data_5Field = value;
        
    
    
    /// <remarks/>
    public string Some_Data_1 
        get 
            return this.some_Data_1Field;
        
        set 
            this.some_Data_1Field = value;
        
    
    
    /// <remarks/>
    public string Some_Data_2 
        get 
            return this.some_Data_2Field;
        
        set 
            this.some_Data_2Field = value;
        
    
    
    /// <remarks/>
    [System.Xml.Serialization.XmlTextAttribute()]
    public string[] Text 
        get 
            return this.textField;
        
        set 
            this.textField = value;
        
    
    
    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public byte id 
        get 
            return this.idField;
        
        set 
            this.idField = value;
        
    

【讨论】:

以上是关于如何在c#中使用具有相同名称但不同属性和结构的元素反序列化XML的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XmlSerializer 类对象将具有相同名称的 2 个子元素组合成单个属性是 C#

使用 SAX 解析器,如何解析具有相同名称标签但元素不同的 xml 文件?

C#中具有相同名称和签名但返回类型不同的方法

如何反序列化具有相同名称但不同类型的 API 响应

如何将内容不同但结构相同的 JSON 字符串转换为 C# 对象?

如何从 html 源代码中具有相同属性集和相同层次结构的 2 个元素中抓取单个元素(使用 python 的美丽汤)