Обзор
XML-документы представляют собой иерархические документы, в которых одни и те же имена элементов и пространства имен могут встречаться в нескольких местах, иметь разное значение и иметь бесконечную глубину (рекурсивно). Обычно решение больших проблем состоит в том, чтобы разделить их на маленькие проблемы. В контексте синтаксического анализа XML это означает анализ определенных частей XML в методах, специфичных для этого XML. Например, одна часть логики проанализирует адрес:
<Address>
<Street>Odins vei</Street>
<Building>4</Building>
<Door>b</Door>
</Address>
то есть у вас будет метод
AddressType parseAddress(...); // A
or
void parseAddress(...); // B
где-то в вашей логике, принимая аргументы входных XML-данных и возвращая объект (результат B можно будет извлечь из поля позже).
SAX
SAX "подталкивает" XML events, оставляя вам право определять место событий XML в вашей программе / данных.
// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
// .. your logic here for start element
}
В случае начального элемента «Building» вам нужно будет определить, что вы действительно анализируете Address, а затем направить событие XML методу, задача которого - интерпретировать Address.
StAX
StAX "вытягивает" XML events, оставляя вам право определять, где в вашей программе / данных получать события XML.
// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
// .. your logic here for start element
}
Конечно, вы всегда хотели бы получать событие «Building» в методе, задачей которого является интерпретация Address.
Обсуждение
Разница между SAX и StAX заключается в том, что они проталкивают и вытягивают. В обоих случаях состояние синтаксического анализа необходимо как-то обрабатывать.
Это означает, что метод B является типичным для SAX, а метод A - для StAX. Кроме того, SAX должен передавать B отдельные события XML, в то время как StAX может передавать несколько событий A (путем передачи экземпляра XMLStreamReader).
Таким образом, B сначала проверяет предыдущее состояние синтаксического анализа, а затем обрабатывает каждое отдельное событие XML, а затем сохраняет состояние (в поле). Метод A может просто обрабатывать все события XML одновременно, обращаясь к XMLStreamReader несколько раз, пока не будет удовлетворен.
Заключение
StAX позволяет структурировать код синтаксического анализа (привязки данных) в соответствии со структурой XML; поэтому в отношении SAX «состояние» неявно определяется потоком программы для StAX, тогда как в SAX вам всегда нужно сохранять какую-то переменную состояния + направлять поток в соответствии с этим состоянием для большинства вызовов событий.
Я рекомендую StAX для всех документов, кроме самых простых. Лучше перейти к SAX в качестве оптимизации позже (но к тому времени вы, вероятно, захотите перейти на двоичный).
Следуйте этому шаблону при синтаксическом разборе с помощью StAX:
public MyDataBindingObject parse(..) { // provide input stream, reader, etc
// set up parser
// read the root tag to get to level 1
XMLStreamReader reader = ....;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
// check if correct root tag
break;
}
// add check for document end if you want to
} while(reader.hasNext());
MyDataBindingObject object = new MyDataBindingObject();
// read root attributes if any
int level = 1; // we are at level 1, since we have read the document header
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
// do stateful stuff here
// for child logic:
if(reader.getLocalName().equals("Whatever1")) {
WhateverObject child = parseSubTreeForWhatever(reader);
level --; // read from level 1 to 0 in submethod.
// do something with the result of subtree
object.setWhatever(child);
}
// alternatively, faster
if(level == 2) {
parseSubTreeForWhateverAtRelativeLevel2(reader);
level --; // read from level 1 to 0 in submethod.
// do something with the result of subtree
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
// do stateful stuff here, too
}
} while(level > 0);
return object;
}
Таким образом, субметод использует примерно тот же подход, то есть уровень подсчета:
private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySubTreeObject object = new MySubTreeObject();
// read element attributes if any
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
// do stateful stuff here
// for child logic:
if(reader.getLocalName().equals("Whatever2")) {
MyWhateverObject child = parseMySubelementTree(reader);
level --; // read from level 1 to 0 in submethod.
// use subtree object somehow
object.setWhatever(child);
}
// alternatively, faster, but less strict
if(level == 2) {
MyWhateverObject child = parseMySubelementTree(reader);
level --; // read from level 1 to 0 in submethod.
// use subtree object somehow
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
// do stateful stuff here, too
}
} while(level > 0);
return object;
}
И затем, в конце концов, вы достигнете уровня, на котором вы будете читать базовые типы.
private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySetterGetterObject myObject = new MySetterGetterObject();
// read element attributes if any
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
// assume <FirstName>Thomas</FirstName>:
if(reader.getLocalName().equals("FirstName")) {
// read tag contents
String text = reader.getElementText()
if(text.length() > 0) {
myObject.setName(text)
}
level--;
} else if(reader.getLocalName().equals("LastName")) {
// etc ..
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
// do stateful stuff here, too
}
} while(level > 0);
// verify that all required fields in myObject are present
return myObject;
}
Это довольно просто, и здесь нет места недоразумениям. Только не забудьте правильно понизить уровень:
A. после того, как вы ожидали символы, но получили END_ELEMENT в некотором теге, который должен содержать символы (в приведенном выше шаблоне):
<Name>Thomas</Name>
был вместо
<Name></Name>
То же самое верно и для отсутствующего поддерева, как вы понимаете.
B. после вызова методов суб-синтаксического анализа, которые вызываются для начальных элементов, и возвращает ПОСЛЕ соответствующего конечного элемента, т.е. синтаксический анализатор находится на один уровень ниже, чем до вызова метода (шаблон выше).
Обратите внимание, как этот подход полностью игнорирует «игнорируемые» пробелы для более надежной реализации.
Парсеры
Используйте Woodstox для большинства функций или Aaalto-xml для скорости.
person
Community
schedule
23.09.2011