Вудстокс пропустить часть XML
Java: 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 ответа
Я нашел причину, почему я получаю исключение IllegalStateException. Очень полезным был ответ на вопрос. Большое спасибо.
Стоит также прочитать ответ, данный Блейзом.
Но добираться до сути дела. Проблема была не в самом методе 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.
Самый простой способ избежать этого исключения - просто вызвать оператор break после вызова метода skipElement(). В этом случае код для получения атрибутов не будет выполнен, поэтому исключение не будет выдано.
if("dad".equals(currentElementName) == true){
logger.info("isStartElement: " + xmlsr.isStartElement());
logger.info("Element BEGIN: " + currentElementName);
xmlsr.skipElement();
break; //the cure for IllegalStateException
}
Извините, я не дал вам возможности ответить на мой первоначальный вопрос из-за большого количества скрытого кода.
Поскольку 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
Я пробовал это в 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
Похоже, что метод xmlsr.skipElement() должен использовать событие XMLEvent2.START_ELEMENT. А поскольку вы уже использовали его (xmlsr.next()), этот метод выдает ошибку.