gpt4 book ai didi

c# - 如何使用 XmlSerializer 反序列化大型文档中的节点

转载 作者:行者123 更新时间:2023-12-05 04:07:33 26 4
gpt4 key购买 nike

我有一个大型 XML 文档,我已将其加载到 XmlDocument 中我想使用 XmlSerializer类以将其中的选定元素反序列化为使用 xsd.exe 生成的 .NET 类。

这是我迄今为止尝试过的 MCVE; xsd 和生成的类在帖子的末尾。如代码注释中所述,我得到一个 InvalidOperationException - <Cars xmlns:'http://MyNamespace' /> was not expected :

static string XmlContent = @"
<RootNode xmlns=""http://MyNamespace"">
<Cars>
<Car make=""Volkswagen"" />
<Car make=""Ford"" />
<Car make=""Opel"" />
</Cars>
</RootNode>";

static void TestMcve()
{
var doc = new XmlDocument();
doc.LoadXml(XmlContent);
var nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("myns", "http://MyNamespace");
var rootSerializer = new XmlSerializer(typeof(RootNode));
var root = (RootNode) rootSerializer.Deserialize(new XmlNodeReader(doc));
Console.WriteLine(root.Cars[0].make); // Works fine so far

var node = doc.DocumentElement.SelectSingleNode("myns:Cars", nsMgr);
Console.WriteLine(node.OuterXml);
var carSerializer = new XmlSerializer(typeof(Car));
using (var reader = new XmlNodeReader(node))
{
// What I want is a list of Car instances deserialized from
// the Car child elements of the Cars element.
// The following line throws an InvalidOperationException
// "<Cars xmlns:'http://MyNamespace' /> was not expected"
// If I change SelectSingleNode above to select "myns:Cars/myns:Car"
// I get "<Car xmlns:'http://MyNamespace' /> was not expected"
var result = carSerializer.Deserialize(reader);
}
}

我还想随后更新我的 Car类实例,并使用 XmlSerializer 将其插入回文档中,这是后续问题的主题 How to insert a node in a large document using XmlSerializer .

xsd 和生成的类如下:

<xs:schema xmlns="http://MyNamespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://MyNamespace"
elementFormDefault="qualified" attributeFormDefault="unqualified"
version="3.9.0.8">
<xs:complexType name="Cars">
<xs:sequence>
<xs:element name="Car" type="Car" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Car">
<xs:attribute name="make" type="xs:string" use="required"/>
</xs:complexType>
<xs:complexType name="RootNode">
<xs:sequence>
<xs:element name="Cars" type="Cars" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:element name="RootNode" type="RootNode" />
</xs:schema>

xsd.exe 生成的代码:

using System.Xml.Serialization;


/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://MyNamespace")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://MyNamespace", IsNullable=false)]
public partial class RootNode {

private Car[] carsField;

/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Car[] Cars {
get {
return this.carsField;
}
set {
this.carsField = value;
}
}
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://MyNamespace")]
public partial class Car {

private string makeField;

/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string make {
get {
return this.makeField;
}
set {
this.makeField = value;
}
}
}

最佳答案

这里有两个问题:

  1. var node = doc.DocumentElement.SelectSingleNode("myns:Cars", nsMgr);位于 <Cars> element -- <Car> 重复序列的容器元素节点——但是你的 XmlSerializer构造为反序列化名为 <Car> 的单个根元素.尝试使用为反序列化单个汽车而构造的序列化器来反序列化一系列汽车是行不通的。

  2. 出于某种原因xsd.exe为您的 Car 生成了一个定义没有 XmlRoot 的类型属性:

    [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://MyNamespace")]
    // Not included!
    //[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://MyNamespace")]
    public partial class Car
    {
    }

    因此,如果您尝试序列化或反序列化 Car 的单个实例作为 XML 文档的根 XML 元素 然后 XmlSerializer将期望该根元素不在任何 namespace 中。每个<Car>大型文档中的节点位于 "http://MyNamespace" 中默认命名空间,因此尝试单独反序列化每个命名空间也行不通。

    您可以手动添加缺失的 [XmlRoot(Namespace = "http://MyNamespace")]属性为 Car ,但如果 XSD 文件随后被修改并且需要重新生成 c# 类型,则必须执行此操作可能会很麻烦。

要避免这两个问题,您可以使用 XmlNode.SelectNodes(String, XmlNamespaceManager) 选择每个 <Car> <Cars> 内的节点元素,然后通过 constructing an XmlSerializer 反序列化每个元素覆盖 XmlRootAttribute带有被反序列化的节点的元素名称和命名空间。首先,定义以下扩展方法:

public static partial class XmlNodeExtensions
{
public static List<T> DeserializeList<T>(this XmlNodeList nodes)
{
return nodes.Cast<XmlNode>().Select(n => n.Deserialize<T>()).ToList();
}

public static T Deserialize<T>(this XmlNode node)
{
if (node == null)
return default(T);
var serializer = XmlSerializerFactory.Create(typeof(T), node.LocalName, node.NamespaceURI);
using (var reader = new XmlNodeReader(node))
{
return (T)serializer.Deserialize(reader);
}
}
}

public static class XmlSerializerFactory
{
// To avoid a memory leak the serializer must be cached.
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
// This factory taken from
// https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648

readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;

static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}

public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}

然后反序列化如下:

var nodes = doc.DocumentElement.SelectNodes("myns:Cars/myns:Car", nsMgr);
var cars = nodes.DeserializeList<Car>();

必须缓存使用覆盖根元素名称或命名空间构造的序列化程序的节点,以避免内存泄漏,如 this answer 中所述。通过 Marc Gravell .

sample 加工 .Net fiddle .

关于c# - 如何使用 XmlSerializer 反序列化大型文档中的节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48652270/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com