【问题标题】:Deserializing a single element in a large XML document: xmlSerializer.Deserialize(xmlReader.ReadSubtree()) fails due to namespace issues反序列化大型 XML 文档中的单个元素:xmlSerializer.Deserialize(xmlReader.ReadSubtree()) 由于命名空间问题而失败
【发布时间】:2015-01-27 23:40:38
【问题描述】:

我正在尝试一次性处理一个大型 XML 文档(使用 XmlReader),并使用 XmlSerializer 仅反序列化其中的某些元素。

下面是一些代码和一个小的模拟 XML 文档,展示了我是如何尝试这样做的。

使用XmlReader 的理由: 1. 我正在处理非常大的 XML 文档(10-250 MB),因此我这样做不想加载到内存中。所以XmlDocument 是不可能的。 2. 我只想提取某些元素。通常我将能够忽略大多数其他内容。 XmlReader 似乎给了我一种跳过不相关内容的有效方法。 3. 我事先不知道我可以处理的任何和所有元素是否会出现;因此我没有使用一堆 Xpath/XQuery 或基于 LINQ to XML 的查询,因为我只想对 XML 文件进行一次传递(由于它们的大小)。

public class ElementOfInterest { }
…

var xml = @"<?xml version='1.0' encoding='utf-8' ?>
            <Root xmlns:ex='urn:stakx:example'
                  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
              <ElementOfInterest xsi:type='ex:ElementOfInterest' />
            </Root>";

var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(xml));
reader.ReadToFollowing("ElementOfInterest");

var serializer = new System.Xml.Serialization.XmlSerializer(typeof(ElementOfInterest));
serializer.Deserialize(reader.ReadSubtree());

最后一行代码抛出如下内部异常:

InvalidOperationException: "命名空间前缀 ex 未定义。"

显然,XmlSerializer 无法识别 xsi:type 属性值中的 ex 命名空间前缀。

这只是我遇到的一个错误,但坦率地说,更大的问题是我不知道如何解决整个命名空间问题。我只是在寻找一种方便的方法来反序列化 XML 文档中的单个节点,但这似乎需要手动注册/管理命名空间,并以某种方式将它们从 XmlReader 转发到 XmlSerializer .

有人可以通过指出我的代码中的错误或通过显示替代方法来演示如何从使用XmlReader 读取的 XML 文档中反序列化单个节点吗?

【问题讨论】:

  • XmlNamespaceManager 上查找示例。从one 开始。
  • @kennyzx:我看过XmlNamespaceManagerXmlNameTableXmlParserContext,等等。我根本不知道它应该如何在我的场景中组合在一起。你能帮我演示一下它的用途吗?

标签: c# xml xml-namespaces xmlreader xmlserializer


【解决方案1】:

以下作品:

using System.IO;
using System.Xml;
using System.Xml.Serialization;

static void Main()
{
    var xml = @"<?xml version='1.0' encoding='utf-8' ?>
                <Root
                  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
                  xmlns:ex='urn:stakx:example'
                >
                  <ex:ElementOfInterest xsi:type='ex:ElementOfInterest' />
                </Root>";

    var nt = new NameTable();
    var mgr = new XmlNamespaceManager(nt);
    mgr.AddNamespace("ex", "urn:stakx:example");
    var ctxt = new XmlParserContext(nt, mgr, "", XmlSpace.Default);
    var reader = XmlReader.Create(new StringReader(xml), null, ctxt);
    var serializer = new XmlSerializer(typeof(ElementOfInterest));

    reader.ReadToFollowing("ElementOfInterest", "urn:stakx:example");
    var eoi = (ElementOfInterest)serializer.Deserialize(reader.ReadSubtree());
}

[XmlRoot(Namespace = "urn:stakx:example")]
public class ElementOfInterest { }

注意输入中的命名空间:&lt;ex:ElementOfInterest&gt;

【讨论】:

  • 你能解释一下为什么你改变了输入文档(即给元素添加了命名空间前缀)吗?是为了让您的代码正常工作,还是因为示例输入的 my 版本格式不正确/在某些方面无效?
  • 两者都有。好吧,您的输入文档指出生成的对象应位于 urn:stakx:example 命名空间中。您的目标类ElementOfInterest 没有反映这一点,因此添加XmlRoot(Namespace=...) 类属性是第一个更改。现在,当您再次序列化 ElementOfInterest 对象时,生成的XML 元素也将位于urn:stakx:example 命名空间中。为了使反序列化和序列化对称,我必须将元素放入输入中的该命名空间。
猜你喜欢
  • 2021-07-02
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 2012-03-30
相关资源
最近更新 更多