代码之家  ›  专栏  ›  技术社区  ›  user306708

如何在Java中转换巨大的XML文件?

  •  7
  • user306708  · 技术社区  · 14 年前

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

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

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

    步骤1使用SAX很容易,对于DOM解析器是不可能的。
    步骤2对SAX很烦人,但是对于DOM解析器或XSLT很容易。

    那又怎么样?-有没有一种结合SAX和DOM解析器来完成任务的好方法?

    7 回复  |  直到 7 年前
        1
  •  9
  •   Chris Lercher    14 年前

    是的,只需编写一个SAX内容处理程序,当它遇到某个元素时,您可以在该元素上构建一个DOM树。我用非常大的文件完成了这项工作,而且效果非常好。

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

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

        2
  •  10
  •   Jon Skeet    14 年前

    StAX 似乎是一个显而易见的解决方案:它是一个pull解析器,而不是SAX的“push”或DOM的“buffer the whole thing”方法。但不能说我用过它。一 "StAX tutorial" search 可能有用:)

        3
  •  5
  •   Christian Semrau Louis Wasserman    14 年前

    我有很好的经验 STX ( XML流式转换 )基本上,它是一个流式的XSLT版本,非常适合以最小的内存占用来解析大量的数据。它在Java中实现了一个名为 Joost .

    应该很容易找到一个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>
    

    这个 pass-through="none" stx:transform 将默认模板(用于节点、属性等)配置为不生成输出,而是处理子元素。然后 stx:template 匹配xpath element/child (这是放置匹配表达式的位置),它在“复制”组中“处理自身”,这意味着来自 group name="copy" 对当前元素调用。那个团体有 pass-though="all" ,因此默认模板复制其输入和处理子元素。当 元素/子元素 元素已结束,控件将传回调用的模板 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 元素。

        4
  •  3
  •   BalusC    14 年前

    既然你说的是GB,我宁愿考虑内存使用的优先顺序。SAX需要的内存大约是文档大小的2倍,而DOM需要的是 至少 5次。因此,如果XML文件是1GB大的,那么DOM将至少需要5GB的可用内存。这不再有趣了。所以SAX(或者它的任何变体,比如stax)是这里最好的选择。

    如果你想要最节省内存的方法,看看 VTD-XML . 只需要 一点 内存比文件大。

        5
  •  2
  •   ilikeorangutans    14 年前

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

        6
  •  2
  •   Daniel Haley    7 年前

    对于这样一个大型的XML文档,具有流式体系结构(如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
  •  0
  •   jarnbjo    14 年前

    使用 XMLEventReader 还有一些 XMLEventWriter 来自javax.xml.stream包的。