【问题标题】:How to transform huge xml files in java?如何在java中转换巨大的xml文件?
【发布时间】:2011-02-15 22:54:06
【问题描述】:

正如标题所说,我有一个巨大的 xml 文件 (GB)

<root>  
<keep>  
   <stuff>  ...  </stuff>  
   <morestuff> ... </morestuff>  
</keep>  
<discard>  
   <stuff>  ...  </stuff>  
   <morestuff> ... </morestuff>
</discard>  
</root>  

我想把它改成一个小得多的只保留几个元素的。
我的解析器应该执行以下操作:
1. 解析文件,直到相关元素开始。
2. 将整个相关元素(带有子元素)复制到输出文件。转到 1。

第 1 步对于 SAX 来说很容易,而对于 DOM 解析器来说是不可能的。
第 2 步对 SAX 来说很烦人,但使用 DOM-Parser 或 XSLT 很容易。

那又怎样? - 有没有一种巧妙的方法可以结合 SAX 和 DOM-Parser 来完成这项任务?

【问题讨论】:

    标签: java xml parsing


    【解决方案1】:

    StAX 似乎是一个明显的解决方案:它是一个拉式解析器,而不是 SAX 的“推送”或 DOM 的“缓冲整个事情”方法。不过不能说我用过。 "StAX tutorial" search 可能会派上用场:)

    【讨论】:

    • +1 如果以前没有接触过处理 XML 文件,StAX 比 SAX 更容易使用。此外,它还允许编写 XML(与 SAX 不同)。
    【解决方案2】:

    是的,只需编写一个 SAX 内容处理程序,当它遇到某个元素时,就在该元素上构建一个 dom 树。我已经对非常大的文件进行了此操作,并且效果很好。

    实际上非常简单:一旦遇到所需元素的开头,就在内容处理程序中设置一个标志,然后从那里将所有内容转发给 DOM 构建器。当遇到元素结束时,将标志设置为 false,并写出结果。

    (对于具有相同元素名称的嵌套元素的更复杂情况,您需要创建堆栈或计数器,但这仍然很容易。)

    【讨论】:

      【解决方案3】:

      我在 STXXML 的流式转换)方面获得了很好的体验。基本上,它是 XSLT 的流式版本,非常适合以最少的内存占用解析大量数据。它有一个名为 Joost 的 Java 实现。

      想出一个 STX 转换应该很容易,它忽略所有元素,直到元素匹配给定的 XPath,复制该元素及其所有子元素(使用模板组中的标识模板),并继续忽略元素直到下一场比赛。

      更新

      我拼凑了一个 STX 转换,可以满足我的理解。它主要依赖于仅 STX 的功能,例如模板组和可配置的默认模板。

      <stx:transform xmlns:stx="http://stx.sourceforge.net/2002/ns"
          version="1.0" pass-through="none" output-method="xml">
          <stx:template match="element/child">
              <stx:process-self group="copy" />
          </stx:template>
          <stx:group name="copy" pass-through="all">
          </stx:group>
      </stx:transform>
      

      stx:transform 处的pass-through="none" 配置默认模板(用于节点、属性等)以不产生输出,但处理子元素。然后stx:template 匹配XPath element/child(这是放置匹配表达式的地方),它在“复制”组中“处理自我”,这意味着来自group name="copy" 的匹配模板在当前元素。该组具有pass-though="all",因此默认模板复制其输入并处理子元素。当element/child 元素结束时,控制权被传递回调用process-self 的模板,并再次忽略以下元素。直到模板再次匹配。

      以下是一个示例输入文件:

      <root>
          <child attribute="no-parent, so no copy">
          </child>
          <element id="id1">
              <child attribute="value1">
                  text1<b>bold</b>
              </child>
          </element>
          <element id="id2">
              <child attribute="value2">
                  text2
                  <x:childX xmlns:x="http://x.example.com/x">
                  <!-- comment -->
                      yet more<b i="i" x:i="x-i" ></b>
                  </x:childX>
              </child>
          </element>
      </root>
      

      这是对应的输出文件:

      <?xml version="1.0" encoding="UTF-8"?>
      <child attribute="value1">
                  text1<b>bold</b>
              </child><child attribute="value2">
                  text2
                  <x:childX xmlns:x="http://x.example.com/x">
                  <!-- comment -->
                      yet more<b i="i" x:i="x-i" />
                  </x:childX>
              </child>
      

      不寻常的格式是跳过包含child 元素之外的换行符的文本节点的结果。

      【讨论】:

      • 听起来不错。我可以只写一个 xslt-stylesheet 然后用 STX 运行它吗?
      • 不,这是不可能的。虽然 XSLT 使用模式来区分不同情况下相同匹配的模板(在您的情况下是跳过模式与复制模式),但 STX 使用模板组。模板中的语法类似于 XSLT,但在细节上有所不同。我在答案中添加了一个示例转换。
      • 请注意,在用于匹配模板的 XPath 中,您可以访问的唯一节点是当前节点、其父节点及其属性。由于转换的流式传输性质,您无法在任何其他先前或后续节点上进行匹配。如果您需要这种匹配,您可以定义变量(可变的)并在stx:if 测试中使用它们。但这很棘手,感觉就像在 XML 中实现内容处理程序。
      【解决方案4】:

      既然您说的是 GB,我宁愿优先考虑内存使用情况。 SAX 需要大约 2 倍于文档大的内存,而 DOM 需要它至少 5 倍。因此,如果您的 XML 文件有 1GB 大,那么 DOM 至少需要 5GB 的可用内存。这已经不好笑了。所以 SAX(或它的任何变体,如 StAX)是这里的最佳选择。

      如果您想要最节省内存的方法,请查看VTD-XML。它只需要比大文件多一点的内存。

      【讨论】:

      • 好点,内存在这里绝对是至关重要的。顺便说一句,SAX 甚至不一定需要两倍的文档大小 - 因为它是一个流式 API,您可以不断地对文档的以前部分进行垃圾收集,只要您不再需要它们。
      • 是的,但这取决于功能要求。例如,他可能需要将整个 XML 保存在内存中,然后才能收集所需的信息。
      【解决方案5】:

      看看StAX,这可能是你需要的。 IBM Developer Works上有很好的介绍。

      【讨论】:

        【解决方案6】:

        对于如此大的 XML 文档,具有流式架构的东西(例如 Omnimark)将是理想的选择。

        它也不必太复杂。如下所示的 Omnimark 脚本可以满足您的需求:

        process
        
        submit #main-input
        
        macro upto (arg string) is
            ((lookahead not string) any)*
        macro-end
        
        find (("<keep") upto ("</keep>") "</keep>")=>keep
            output keep
        
        find any
        

        【讨论】:

          【解决方案7】:

          您可以使用 javax.xml.stream 包中的 XMLEventReader 和几个 XMLEventWriters 轻松完成此操作。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-11-01
            • 2020-11-06
            • 2010-10-29
            • 2014-05-03
            相关资源
            最近更新 更多