深度首次阅读大型XML文件

时间:2012-09-19 12:39:28

标签: xml xslt sax depth-first-search

我需要处理一堆非常大的XML文件并深度读取每个元素。由于大小的原因,任何DOM解决方案都是不可能的,因为所需的实际元素不是“叶子”而是其父级,因此事情变得更加复杂。

更具体地说,文件具有类似

的结构
    <Level 1>
        ...
        <Level 2>
            ...
            <Level N-1>
                <value>...</value>
                <value>...</value>
                ...
                <value>...</value>
            </Level N-1>
            <Level N-1>
                <value>...</value>
                <value>...</value>
                ...
                <value>...</value>
            </Level N-1>
            ...
            <Level N-1>
                <value>...</value>
                <value>...</value>
                ...
                <value>...</value>
            </Level N-1>
            ...
        </Level 2>
    </Level 1>

在如上所述的每个文件中,需要单独读取<Level N-1>元素(每个元素包括所有相应的<value>元素)。深度N在每个文件和文件中都有所不同,因此它们基本上是未知的,XML标记名称也是如此。由于<value>元素也存在于更高层次(即它们不能保证已达到Level N),事情变得更加复杂。

将特定深度的整个XML元素作为字符串读取的快速解决方案类似于

int level = 0;  // The base level of the element, could be at any depth
Reader in = ... // The reader to the input
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
PrintStream out = new PrintStream(outStream);
XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(in);
XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(out);
XMLEvent event;

while ((level > 0) && reader.hasNext());
{
    event = reader.nextEvent();

    if (event.isStartElement())
    {
        level++;
    }
    else if (event.isEndElement())
    {
        level--;
    }

    writer.add(event);
}

writer.flush();

String element = new String(outStream.toByteArray());

但是,如果调用代码不知道已到达Level N-1元素并且它前进到Level N(即,到<value>元素),则上述内容无效。

SAX解决方案是理想的,但即使是通过XSLT模板预处理文件也是可以接受的。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

在纯XSLT 1.0或XSLT 2.0中无法进行有用的XSLT预处理,因为XSLT处理器(1.0或2.0)通常会在内存中生成整个XML文档的表示(不一定是DOM)。

XSLT 3.0 (仍然是WD)中,将会有流式传输作为语言的一部分,但W3C XSLT WG仍在积极开发这个规范尚未实现稳定。

Saxon以流媒体模板的形式 streaming extensions 处于“可流式模式”:

<xsl:mode name="s" streamable="yes"/>

使用它可以生成XML文档,每个文档只包含以“Level N-1”元素为根的子树。

答案 1 :(得分:1)

如果我已正确理解您的问题,那么当您获得<value>标记并完成了关卡标记后,您就难以区分

当您识别某个事件时,您可以获得更多信息,例如名称:

  if (event.isStartElement()) {
    StartElement element = (StartElement) event;
    System.out.println("Start Element: " + element.getName());
  }

如果您真正想要的是此前的最后一级,当然您必须坚持下去。