【问题标题】:Java: How to split XML stream into small XML documents? XPath on streaming XML parser?Java:如何将 XML 流拆分为小的 XML 文档?流式 XML 解析器上的 XPath?
【发布时间】:2009-10-28 22:16:22
【问题描述】:

我需要从网络上读取一个大的 XML 文档并将其拆分为更小的 XML 文档。特别是我从网络读取的流看起来像这样:

<a> <b> ... </b> <b> ... </b> <b> ... </b> <b> ... </b> .... </a>

我需要把它分成几块

&lt;a&gt; &lt;b&gt; ... &lt;/b&gt; &lt;a&gt;

(我实际上只需要 &lt;b&gt; .... &lt;/b&gt; 部分,只要将声明更高的命名空间绑定(例如在 &lt;a&gt; 中)移动到 &lt;b&gt; 如果这样更容易)。

文件对于 DOM 样式解析器来说太大了,必须进行流式处理。是否有任何 XML 库可以做到这一点?

[编辑]

我认为我理想中寻找的是能够在 XML 流上执行 XPath 查询的能力,其中流解析器只解析返回结果节点集中的下一项(及其所有属性)和儿童)。不必一定是 XPath,但一定要符合这个想法。

谢谢!

【问题讨论】:

    标签: java xml streaming


    【解决方案1】:

    带有 SAX 过滤器的 JAXP SAX api 既快速又高效。好的介绍过滤器可以看到here

    【讨论】:

    • 嗯,我不太明白。我可以看到当我的标签被解析时如何捕获事件,但我不清楚如何让过滤器将流重定向到新文档,直到结束标签以及如何包含父母+他们的各种名称空间绑定。你有没有机会对此进行扩展?我知道我可以通过 SAX 来做到这一点,基本上可以捕获各种事件并跟踪事物并简单地复制事物,但我希望有一个更简单的解决方案。
    • 这不是我希望的简单解决方案,但它是正确的,没有人有更好的建议,所以我给你...
    【解决方案2】:

    作为 XML 拆分器,VTD-XML 非常适合这项任务……它也比 DOM 更节省内存。简化编码的关键方法是 VTDNav 的 getElementFragment()... 下面是将 input.xml 拆分为 out0.xml 和 out1.xml 的 Java 代码

    <a> <b> text1 </b>  <b> text2 </b> </a>
    

    进入

    <a> <b> text1</b> </a> 
    

    <a> <b> text2</b> </a>
    

    使用 XPath

    /a/b
    

    代码

    import java.io.*;
    import com.ximpleware.*;
    
    public class split {
        public static void main(String[] argv) throws Exception{
            VTDGen vg = new VTDGen();
            if (vg.parseFile("c:/split/input.xml", true)){
                VTDNav vn = vg.getNav();
                AutoPilot ap = new AutoPilot(vn);
                ap.selectXPath("/a/b");
                int i=-1,k=0;
                byte[] ba = vn.getXML().getBytes();
                while((i=ap.evalXPath())!=-1){
                    FileOutputStream fos = new FileOutputStream("c:/split/out"+k+".xml");
                    fos.write("<a>".getBytes());
                    long l = vn.getElementFragment();
                    fos.write(ba, (int)l, (int)(l>>32));
                    fos.write("</a>".getBytes());
                    k++;
                }
            }       
        }
    }
    

    更多阅读请访问http://www.devx.com/xml/Article/36379

    【讨论】:

    • 感谢您的回复。这对我来说看起来像是一种 DOM 风格的方法,在进行查询之前需要重新解析整个文档。我的 XML 流太大了,需要流解析器来完成。
    • 加长版,可以通过内存映射进行部分加载,不过这个只有加长版,标准版,最多2GB,只消耗1/5左右DOM 的记忆...
    • 这是您的代码片段(VTDGen.parseFile() 方法):fis = new FileInputStream(f);字节[] b = 新字节[(int) f.length()];。因此,您将所有文件加载到内存中。这真是令人作呕。
    【解决方案3】:

    去老学校

    StringBuilder buffer = new StringBuilder(1024 * 50);
    BufferedReader reader = new BufferedReader(new FileReader(pstmtout));
    String line;
    while ((line = reader.readLine()) != null) {
      buffer.append(line);
      if (line.equalsIgnoreCase(endStatementTag)) {
        service.handle(buffer.toString());
        buffer.delete(0, buffer.length());
      }
    }
    

    【讨论】:

      【解决方案4】:

      你可以用 XProc 语言做到这一点

      <?xml version="1.0" encoding="ISO-8859-1"?>
      <p:declare-step xmlns:p="http://www.w3.org/ns/xproc" version="1.0">
        <p:load href="in/huge-document.xml"/>
        <p:for-each>
          <p:iteration-source select="/a/b"/>
          <p:wrap match="/b" wrapper="a"/>
          <p:store>
             <p:with-option name="href" select="concat('part', p:iteration-position(), '.xml')">
                <p:empty/>
             </p:with-option>
          </p:store>
        </p:for-each>
      </p:declare-step>
      

      您也可以使用 QuiXProc(流式 XProc 实现:http://code.google.com/p/quixproc/)尝试流式传输它

      【讨论】:

        【解决方案5】:

        我碰巧喜欢XOM XML 库,因为它的界面简单、直观且功能强大。要使用 XML 做您想做的事,您可以使用自己的 NodeFactory 并(例如)覆盖 finishMakingElement() 方法。如果它正在制作你想要的元素(在你的情况下,&lt;b&gt;),那么你将它传递给你需要做的任何事情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-10
          • 2012-01-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多