如何在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 文件?