【问题标题】:Create an XDocument from specific elements of a larger XML using XmlReader使用 XmlReader 从较大 XML 的特定元素创建 XDocument
【发布时间】:2017-07-26 15:36:13
【问题描述】:

我有一个非常大的 XML 文件 (> 100MB),我只想通过 Linq to XML 查看其中的一些小元素。

我知道有很多方法可以做到这一点,但我想把它保存在内存中,以便在我的程序中的不同地方使用,所以,我想做的是用 @987654321 遍历这个文档一次@ 并创建我自己的 XDocument 变量,其中仅包含我需要查看的那些元素,但我不确定如何正确执行此操作。

以下是我目前掌握的基本代码:

XDocument parsedXml = new XDocument();

using (var rdr = new XmlTextReader(fileName))
{
    rdr.MoveToContent();
    rdr.Read();

    while (rdr.NodeType == XmlNodeType.Element)
    {
        switch (rdr.Name)
        {
            case "Node1":
            case "Node2":
            case "Node3":
                XElement newNode = XElement.Load(rdr.ReadSubtree());
                parsedXml.Add(newNode);
                rdr.Read();
                break;
            default:
                rdr.Skip();
                break;
        }
    }
    rdr.Close();
}

它适用于我添加的第一个元素,但是当我尝试添加任何其他节点时出现错误 - This operation would create an incorrectly structured document

最好的方法是什么?

谢谢!!

【问题讨论】:

  • 如果您的结果是一个新的 Xml/XDocument,我会选择 Xslt,但这只是个人喜好,可能会稍微提高性能。
  • 问题,@Filburt,然后我将整个文档加载到内存中。此方法通过XMLReader 对其进行流式传输,并且仅将相关节点拉入内存,因此不会耗尽资源。
  • Xslt 也可以作用于 StreamReader,我不希望它消耗比 XmlTextReader 更多的资源。
  • @Filburt,我不确定我是否理解 - 我需要我的最终值是一个 XDocument,你是说你可以使用不会增加 XMLReader 内存消耗的 XSLT 创建它?你能告诉我怎么做吗? - 我一直认为 XSLT 加载在 DOM 中。

标签: c# xml linq linq-to-xml


【解决方案1】:

我想出了这个作为我的解决方案 - 如果有更好的方法,请告诉我/发布您的解决方案,但如果没有,我希望这对其他人有所帮助....

XDocument parsedXml = new XDocument();
parsedXml.AddFirst(new XElement("root"));

using (var rdr = new XmlTextReader(fileName))
{
    rdr.MoveToContent();
    rdr.Read();

    while (rdr.NodeType == XmlNodeType.Element)
    {
        switch (rdr.Name)
        {
            case "Node1":
            case "Node2":
            case "Node3":
                XElement newNode = XElement.Load(rdr.ReadSubtree());
                parsedXml.Root.Add(newNode);
                rdr.Read();
                break;
            default:
                rdr.Skip();
                break;
        }
    }
    rdr.Close();
}

基本上,创建一个根节点:

parsedXml.AddFirst(new XElement("root"));

然后专门添加到它:

parsedXml.Root.Add(newNode);

希望这对其他人也有帮助!

【讨论】:

  • 这是正确的解决方案——一个有效的 XML 文件必须包含一个且仅一个 root element,并且 LINQ-to-XML API 强制执行此要求。有关非常相似的问题,请参阅 here
  • 另外,您可以考虑使用XNode.ReadFrom()。如果您使用ReadSubtree(),则必须在推进外部阅读器之前关闭或处置子树阅读器,如in the docs 所述。
  • 在@dbc 的关于XNode.ReadFrom() 的链接之后,我找到了How to: Perform Streaming Transform of Large XML Documents (C#),因此您可能希望将此作为推荐的解决方案。
  • 谢谢,@Filburt,它几乎成了我实现的东西,尽管你的想法让我将 Saxon 视为一种可能的流式 XSLT 解决方案......虽然它与我所知道的非常不同,但是看起来很有希望!!再次感谢!!
【解决方案2】:

使用 Xslt 就像这样简单:

XDocument parsedXml = new XDocument();

using (var xr = XmlReader.Create(fileName))
{
    using (var xw = parsedXml.CreateWriter())
    {
        XslCompiledTransform ct = new XslCompiledTransform();
        ct.Load(xsltFileName);
        ct.Transform(fileName, xw);
    }
}

我尝试进行一些快速分析,但我手头没有合适的 Xml 源,因此您可能想尝试使用您的真实数据。

虽然您可能简化的示例肯定不需要复杂的转换,但 Xslt 很快胜过现实世界数据所需的 switch-caseifs。

还有一点值得注意的是使用XmlReader.Create() 而不是XmlTextReader() 中提到的Why is my new XmlTextReader(stream) reading in many megabytes into memory rather than streaming properly?

【讨论】:

  • Filburt,我在网上看,看到大多数人都说它会将整个 DOM 加载到内存中 - 例如这里:social.msdn.microsoft.com/Forums/en-US/… 不是这样吗?因为我同意你的观点,这将是一种非常巧妙的实现方式!
猜你喜欢
  • 2021-09-03
  • 2016-12-03
  • 2013-09-24
  • 1970-01-01
  • 2012-02-11
  • 2013-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多