woodstox跳过xml的一部分

时间:2013-01-14 21:03:15

标签: java xml xml-parsing woodstox

Java:1.6
Woodstox: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();
            }

                    ...
    }
}

我们只是找到元素爸爸的开头,然后跳过它。 但不是那么快,因为Exception将被抛出。这是输出:

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

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

提前谢谢你 休伯特

4 个答案:

答案 0 :(得分:2)

我在java 1.6(jdk1.6.0_30)中尝试使用woodstox-core-lgpl-4.1.4.jar,stax2-api-3.1.1.jar在库路径上。 我的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

答案 1 :(得分:2)

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

<强>演示

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

答案 2 :(得分: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看起来没问题,因为可以为START_ELEMENT和ATTRIBUTE执行getAttributeCount(),getAttributeValue()等方法。

但是在调用方法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
        }

对不起,我没有机会回答我原来的问题,因为隐藏了很多代码。

答案 3 :(得分:0)

看起来xmlsr.skipElement()方法必须使用XMLEvent2.START_ELEMENT事件。由于你已经使用它(xmlsr.next()),该方法会引发错误。