如何在Delphi中使用MSXML SAX将大型XML文件合并

8

编辑: 我(不完整且非常粗糙的)XmlLite头文件翻译可在GitHub上找到。

在Delphi中,使用MSXML而不使用DOM,最好的方法来简单合并大量的XML文档是什么?我应该使用COM组件SAXReader和XMLWriter吗?是否有任何好的例子可以参考?

转换是从许多大文件(60MB+)的根(容器)的所有内容元素简单组合成一个巨大的文件(~1GB)。

<Container>
    <Contents />
    <Contents />
    <Contents />
</Container>

我已经在以下的C#代码中使用XmlWriter和XmlReaders使其工作,但是它需要在本地的Delphi进程中发生:

var files = new string[] { @"c:\bigFile1.xml", @"c:\bigFile2.xml", @"c:\bigFile3.xml", @"c:\bigFile4.xml", @"c:\bigFile5.xml", @"c:\bigFile6.xml" };

using (var writer = XmlWriter.Create(@"c:\HugeOutput.xml", new XmlWriterSettings{ Indent = true }))
{
    writer.WriteStartElement("Container");

    foreach (var inputFile in files)
        using (var reader = XmlReader.Create(inputFile))
        {
            reader.MoveToContent();
            while (reader.Read())
                if (reader.IsStartElement("Contents"))
                    writer.WriteNode(reader, true);
        }

    writer.WriteEndElement(); //End the Container element
}

我们已经在系统的其他部分使用了MSXML DOM,如果可能的话我不想添加新组件。

1
你想使用SAX来避免消耗几个G的内存吗?这个带有MSXML演示的SAX能帮到你吗?http://keith-wood.name/DelphiXML/BookCode/Chapter%2013/index.html - Warren P
是的,Delphi只编译32位,并且基于DOM的MSXML TXMLDocument包装器在文档达到约100MB时会出现EOutOfMemory错误。 - carlmon
我的观点是完全放弃MSXML,转而使用OmniXML。 :-) 在任何合理设计的XML引擎中,您应该能够将1GB的XML文件加载到32位进程中。 - Warren P
这是一个大型企业系统,我们已经在使用MSXML。添加/切换组件涉及到依赖关系、测试和培训等全新的问题...前提是我能说服我们的架构师加入进来。 - carlmon
2
@warren SAX是处理大数据的最佳选择。DOM在32位地址空间中处理大数据时效率很低。 - David Heffernan
显示剩余5条评论
4个回答

3

XmlLite是System.Xml的本地C++端口,提供了拉取解析编程模型。它已经内置于W2K3 SP2、WinXP SP3及以上版本中。在进行从C#到Delphi的1-1映射之前,您需要进行一个Delphi头文件翻译。


1
Delphi/Object Pascal持久化框架tiOPF(http://wiki.freepascal.org/tiOPF)支持XmlLite,因此我想这个开源项目已经包含了头文件翻译。 - mjn
谢谢Samuel,MS XmlLite很好用!tiOPF似乎有另一个叫做XmlLite的东西(或者我找不到这个单元),所以我为我需要的部分编写了自己的头文件翻译。 - carlmon
1
@carlmon:也许你可以分享一下你的头文件翻译? - jpfollenius
@Smasher 这很粗糙,但我创建了一个存储库:https://github.com/GenasysTechnologies/Delphi-XmlLite - carlmon
1
@carlmon 我修复了一些声明,希望现在可以支持win64。此外,我考虑不再关心2010年之前的Delphi和2.6.0之前的FPC。请参见https://github.com/the-Arioch/Delphi-XmlLite/commit/1713b1cb33fe8965f1b4e009255365ba22e24dac中的注释。 - Arioch 'The

1
我会使用常规的文件I/O将一个 写入文本文件,将每个内容作为字符串写入,最后再写入 。 如果您有更合理的大小,我会将所有内容组装成一个字符串列表,然后将其流式传输到磁盘。但是,如果您进入GB领域,那将是有风险的。

1
Delphi中的SAX-with-MSXML功能肯定是可用的,不是吗? - Warren P
我可能会采用这种方法,但我忘了在需要忽略输出的文件中提到一个可变大小的头元素。这使得直接使用文件流有点不太合适... - carlmon
1
使用这种方法而不是使用经过测试的工作SAX解析器是愚蠢的。(除非我从头开始发明它们,否则我不会使用新组件?) - Warren P

1

我几年前为Delphi 5编写了自己的LibXML包装器,但是我们在较新的Delphi上标准化使用MSXML以避免膨胀和依赖性——曾经有一段时间我们链接或者发布3种不同的XML引擎。 - carlmon
现在你只剩下一个版本,它是最有bug的版本,并且它是操作系统的一部分,而不是与你的应用程序一起发布一个已知良好的版本。 :-) - Warren P

0

因为需要一些空间和格式,所以将此作为答案发布。

我有一个非常糟糕的数据文件用于测试,请参见 https://github.com/the-Arioch/omnixml/commit/d1a544048e86921983fced67c772944f12cb1427 上的消息。

在 XE2 调试版本中,OmniXML 有点糟糕:

  • 比 TXmlDocument/MSXML 使用更多约 25% 的内存。修复 .NextSibling 问题后可能会更多,未重新测试。
  • 加载文件时间更长(另一方面,读取节点属性速度显著更快:它们已经是 Delphi 类型的变量,无需跨越 MSXML/Delphi 边界)
  • 绝对不支持命名空间,这使得识别标记变得更加困难
  • XPath 处于萌芽状态,再次包括缺乏命名空间

https://docs.google.com/spreadsheets/d/1QcFVwh3fFfaDyRmv2b-n4Rq4_u5p42UfNbR_FZgZizY/edit?usp=sharing


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接