在 C# 中解析大 Xml 字符串的最有效方法(扩展 DTD 引用、添加新行等)

most efficient way in c# to parse a large Xml string (to expand DTD references, add new lines etc)

我有一个接口提供大 Xml 字符串,这些字符串有效 XML 但可能不是标准格式(比如缺少指定默认名称 space 的前缀)或没有任何 line-endings 或需要扩展 in-lined DTD 中的实体。基本上我需要使用可以处理 in-lined DTD 定义的标准 Xml 解析器来解析这些字符串。此字符串数据可以是从几个字符到千兆字节的任何位置。

目前我正在使用以下代码(如此简单的解析似乎能够解决我上面提到的问题):

              XDocument doc = XDocument.Parse(LargeXmlString);

                var settings = new XmlWriterSettings();
                settings.Indent = true;
                settings.Encoding = Encoding.Unicode;
                //more settings

                StringBuilder parsedOutput = new StringBuilder();
                using (XmlWriter xmlWriter =       
                          XmlWriter.Create(parsedOutput, settings))
                {
                    doc.WriteTo(xmlWriter);
                }

虽然这很容易使用,但我不确定 good/bad 与使用其他一些 .net xml 解析 类 如 XmlReader/XmlTextReader 或 Xml文件等?

使用 .net/c# 支持 类 的 best/most 有效方法是什么(可能无需编写大量新代码)?

感谢您的帮助

`<?xml version="1.0" encoding="UTF-8"?><Catalogue    xmlns="http://www.somewhere.org/BookCatalogue" xmlns:cat="http://www.somewhere.org/BookCatalogue" xmlns:html="http://www.somewhere.org/HTMLCatalogue" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.somewhere.org/BookCatalogue                         txjsgen14.txt"><cat:Magazine><Title>Natural Health</Title><Author>October</Author><Date>December, 1999</Date><Volume>12</Volume>.....`

转换为

`<?xml version="1.0" encoding="utf-8"?>
<cat:Catalogue xmlns="http://www.somewhere.org/BookCatalogue" xmlns:cat="http://www.somewhere.org/BookCatalogue" xmlns:html="http://www.somewhere.org/HTMLCatalogue" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.somewhere.org/BookCatalogue                         txjsgen14.txt">
  <cat:Magazine>
    <cat:Title>Natural Health</cat:Title>
    <cat:Author>October</cat:Author>
    <cat:Date>December, 1999</cat:Date>
    <cat:Volume>12</cat:Volume>
    <cat:htmlTable>.....`

注意根据名称 space 声明向标题和其他元素添加 cat 前缀

谢谢大家的回复。

@谜团 很抱歉我在混乱中造成的混乱。 实际上,我只需要一个字符串到字符串的转换,其中第一个字符串的 not-so-proper XML 格式不正确,没有扩展 DTD 实体,没有行分隔符并且可能缺少前缀等。而第二个字符串应该已经解决了所有这些问题。
现在,如果某些组件(比如 XmlReader)可以将第一个字符串作为参数并将其设为 canonical/properly formatted/expanded XML 和 return 作为字符串,那么我只需要是一个组件。在上面的示例中,解析由 XDocument 完成,格式化由 XmlWriter 完成。而且我什至不确定是谁扩展了实体,是解析器还是 XmlWriter。可能是作者。

目前我会尝试结合使用 XmReader 和 XmlWriter,其中 XmlReader 读取第一个字符串,XmlWriter 写入格式化后的字符串(由用于 XmlWriter 的 XmlWriterSettings 指定)。如果有更好的方法,请告诉我。

对于大型 xml 总是使用 XmlReader 来防止内存不足错误。下面的代码使用 xml linq

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string xml =
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<cat:Catalogue xmlns=\"http://www.somewhere.org/BookCatalogue\" xmlns:cat=\"http://www.somewhere.org/BookCatalogue\" xmlns:html=\"http://www.somewhere.org/HTMLCatalogue\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.somewhere.org/BookCatalogue                         txjsgen14.txt\">" +
                  "<cat:Magazine>" +
                    "<cat:Title>Natural Health</cat:Title>" +
                    "<cat:Author>October</cat:Author>" +
                    "<cat:Date>December, 1999</cat:Date>" +
                    "<cat:Volume>12</cat:Volume>" +
                    "<cat:htmlTable>" +
                    "</cat:htmlTable>" +
                  "</cat:Magazine>" +
                  "<cat:Magazine>" +
                    "<cat:Title>Natural Health</cat:Title>" +
                    "<cat:Author>October</cat:Author>" +
                    "<cat:Date>December, 1999</cat:Date>" +
                    "<cat:Volume>12</cat:Volume>" +
                    "<cat:htmlTable>" +
                    "</cat:htmlTable>" +
                  "</cat:Magazine>" +
                "</cat:Catalogue>";
            StringReader sReader = new StringReader(xml);
            XmlReader xReader = XmlReader.Create(sReader);

            xReader.MoveToContent();
            XNamespace ns = xReader.LookupNamespace(xReader.Prefix);


            while (!xReader.EOF)
            {
                if (xReader.LocalName != "Magazine")
                {
                    xReader.ReadToFollowing("Magazine", ns.NamespaceName);
                }
                if(!xReader.EOF)
                {
                    XElement magazine = (XElement)XElement.ReadFrom(xReader);
                    string title = (string)magazine.Element(ns + "Title");
                }
            }

        }
    }
}

您基本上可以执行示例中的操作,但使用 XmlReader:

XmlReader xmlReader = ...;

using (XmlWriter xmlWriter = ...)
{
    xmlWriter.WriteNode(reader, true);
}

这将是最有效的方式 -- 逐个节点地流式传输文档与在写出之前将整个内容读入内存。