3

爪哇:1.6
伍德斯托克斯:4.1.4

我只想在解析时跳过部分 xml 文件。让我们看一下那个简单的xml:

<family>
    <mom>
        <data height="160"/>
    </mom>
    <dad>
        <data height="175"/>
    </dad>
</family>

我只想跳过爸爸元素。所以看起来像使用如下所示的 skipElement 方法是一个好主意:

FileInputStream fis = ...;
XMLStreamReader2 xmlsr = (XMLStreamReader2) xmlif.createXMLStreamReader(fis);

String currentElementName = null;
while(xmlsr.hasNext()){

    int eventType = xmlsr.next();

    switch(eventType){

        case (XMLEvent2.START_ELEMENT):
            currentElementName = xmlsr.getName().toString();

            if("dad".equals(currentElementName) == true){
                logger.info("isStartElement: " + xmlsr.isStartElement());
                logger.info("Element BEGIN: " + currentElementName);
                xmlsr.skipElement();
            }

                    ...
    }
}

我们只是找到元素爸爸的开始,然后跳过它。但不是那么快,因为会抛出异常。这是输出:

isStartElement: true
Element BEGIN: dad
Exception in thread "main" java.lang.IllegalStateException: Current state not START_ELEMENT

这不是预期的。这确实很意外,因为方法 skipElement 是在 START_ELEMENT 状态下执行的。我不知道发生了什么,也许你知道更多:)。所以请帮助我。

提前感谢
休伯特

4

4 回答 4

2

我在库路径上使用woodstox-core-lgpl-4.1.4.jar、stax2-api-3.1.1.jar 在java 1.6(jdk1.6.0_30)中尝试了这个。我的java文件是这样的:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.XMLStreamReader2;
import org.codehaus.stax2.evt.XMLEvent2;

public class Skip {

    public static void main(String[] args) throws FileNotFoundException,
            XMLStreamException {
        System.setProperty("javax.xml.stream.XMLInputFactory",
                "com.ctc.wstx.stax.WstxInputFactory");
        System.setProperty("javax.xml.stream.XMLOutputFactory",
                "com.ctc.wstx.stax.WstxOutputFactory");
        System.setProperty("javax.xml.stream.XMLEventFactory",
                "com.ctc.wstx.stax.WstxEventFactory");

        FileInputStream fis = new FileInputStream(new File("family.xml"));
        XMLInputFactory xmlif = XMLInputFactory.newFactory();
        XMLStreamReader2 xmlsr = (XMLStreamReader2) xmlif
                .createXMLStreamReader(fis);

        String currentElementName = null;
        while (xmlsr.hasNext()) {

            int eventType = xmlsr.next();

            switch (eventType) {

            case (XMLEvent2.START_ELEMENT):
                currentElementName = xmlsr.getName().toString();

                if ("dad".equals(currentElementName) == true) {
                    System.out.println("isStartElement: "
                            + xmlsr.isStartElement());
                    System.out.println("Element BEGIN: " + currentElementName);
                    xmlsr.skipElement();
                }
                else {
                    System.out.println(currentElementName);
                }

            }
        }
    }
}

奇迹般有效。输出是

family
mom
data
isStartElement: true
Element BEGIN: dad
于 2013-01-14T21:42:24.053 回答
2

由于 Woodstox 是符合 StAX (JSR-173) 的解析器,您可以使用 StAXStreamFilter排除与某些元素对应的事件。我更喜欢这种方法,这样您就可以将过滤逻辑与应用程序逻辑分开。

演示

import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        XMLInputFactory xif = XMLInputFactory.newFactory();
        StreamSource xml = new StreamSource("src/forum14326598/input.xml");
        XMLStreamReader xsr = xif.createXMLStreamReader(xml);
        xsr = xif.createFilteredReader(xsr, new StreamFilter() {

            private boolean accept = true;

            @Override
            public boolean accept(XMLStreamReader reader) {
                if((reader.isStartElement() || reader.isEndElement()) && "dad".equals(reader.getLocalName())) {
                    accept = !accept;
                    return false;
                } else {
                    return accept;
                }
            }

        });

        while(xsr.hasNext()) {
            if(xsr.isStartElement()) {
                System.out.println("start: " + xsr.getLocalName());
            } else if(xsr.isCharacters()) {
                if(xsr.getText().trim().length() > 0) {
                    System.out.println("chars: " + xsr.getText());
                }
            } else if(xsr.isEndElement()) {
                System.out.println("end: " + xsr.getLocalName());
            }
            xsr.next();
        }
    }

}

输出

start: family
start: mom
start: data
end: data
end: mom
end: family
于 2013-01-14T21:56:56.173 回答
2

我找到了我得到 IllegalStateException 的原因。非常有用的是flup的回答。非常感谢。
Blaise 给出的答案也值得一读。

但要切入问题的核心。问题不在于 skipElement() 方法本身。该问题是由用于读取属性的方法引起的。我的问题中有三个点(...)。那么让我们看看那里有什么:

switch(eventType){

case (XMLEvent2.START_ELEMENT):
    currentElementName = xmlsr.getName().toString();
    logger.info("currentElementName: " + currentElementName);


    if("dad".equals(currentElementName) == true){
        logger.info("isStartElement: " + xmlsr.isStartElement());
        logger.info("Element BEGIN: " + currentElementName);
        xmlsr.skipElement();
    }


    case (XMLEvent2.ATTRIBUTE):
        int attributeCount = xmlsr.getAttributeCount(); 
        ...
        break;


}

重要的事情。START_ELEMENT 没有中断语句。因此,每次 START_ELEMENT 事件发生时,也会执行事件 ATTRIBUTE 的代码。根据 Java Docs,这看起来不错,因为方法 getAttributeCount()、getAttributeValue() 等可以为 START_ELEMENT 和 ATTRIBUTE 执行。

但是在调用方法skipElement() 之后,事件START_ELEMENT 更改为END_ELEMENT。因此不允许调用方法 getAttributeCount()。此调用是引发 IllegalStateException 的原因。

避免该异常的最简单方法是在调用 skipElement() 方法后调用 break 语句。在这种情况下,获取属性的代码将不会被执行,因此不会抛出异常。

        if("dad".equals(currentElementName) == true){
            logger.info("isStartElement: " + xmlsr.isStartElement());
            logger.info("Element BEGIN: " + currentElementName);
            xmlsr.skipElement();
            break;                  //the cure for IllegalStateException
        }

很抱歉,由于隐藏了太多代码,我没有机会回答我原来的问题。

于 2013-01-15T15:07:20.863 回答
0

看起来方法 xmlsr.skipElement() 是必须使用 XMLEvent2.START_ELEMENT 事件的方法。而且由于您已经使用了它(xmlsr.next()),因此该方法会引发错误。

于 2013-01-14T21:13:40.530 回答