- PVSM.RU - https://www.pvsm.ru -
Здравствуйте!
Несмотря на снижение популярности формата XML с начала 2000х, он прочно занял свои ниши. Я сталкивался с обработкой XML ~ в 60% проектов и посвятил ей занятие своей стажировки Masterjava. Наиболее частые его применения: XHTML, SOAP, различные конфигурации (например Tomcat, SoapUI, IntelliJ IDEA, Spring XML конфигурация), импорт-экспорт данных. В Java есть несколько API для работы с XML и для разработчика важно понимать, какое из API требуется выбрать в каждой конкретной ситуации. В этой статье я кратко перечислю все Java XML API, их назначение и примеры использования, и подробнее остановлюсь на работе с достаточно редкой, но в ряде случаев единственно верной технологией StAX [1]. Предполагается что с элементами XML [2] вы уже знакомы.
В сравнительной табличке API по их возможностям [18] Easy of Use
для SAX/StAX говорит о том,что автор не умеет работать со StAX и оставшаяся часть статьи будет о том, как «правильно его готовить».
Прежде всего хочу отметить, что со StAX можно работать через 2 API: низкоуровневое XMLStreamReader [19], возвращающая примитивы и высокоуровневое XMLEventReader [20], которое возвращает объекты и расходует больше памяти. Далее я буду работать с XMLStreamReader. Сделав над ним обертку работа с XML будет простой и удобной
Разберем небольшой пример: есть простой XML с городами и юзерами:
<Payload>
<Cities>
<City id="spb">Санкт-Петербург</City>
<City id="mow">Москва</City>
...
</Cities>
...
<Users>
<User city="mow">
<email>gmail@gmail.com</email>
<fullName>Gmail User</fullName>
</User>
<User city="spb">
<email>admin@javaops.ru</email>
<fullName>Admin</fullName>
</User>
...
</Users>
...
</Payload>
В реальности этот XML может содержать сотни городов и сотни тысяч / миллионы юзеров. Все, что требуется: распечатать список городов. В данном случае StAX API — единственно верный выбор. Добавляем в проект вспомогательный класс StaxStreamProcessor
:
public class StaxStreamProcessor implements AutoCloseable {
private static final XMLInputFactory FACTORY = XMLInputFactory.newInstance();
private final XMLStreamReader reader;
public StaxStreamProcessor(InputStream is) throws XMLStreamException {
reader = FACTORY.createXMLStreamReader(is);
}
public XMLStreamReader getReader() {
return reader;
}
@Override
public void close() {
if (reader != null) {
try {
reader.close();
} catch (XMLStreamException e) { // empty
}
}
}
}
Далее мы последовательно идем по XML, считываем все интересные нам события и выводим требуемую информацию:
try (StaxStreamProcessor processor = new StaxStreamProcessor(Files.newInputStream(Paths.get("payload.xml")))) {
XMLStreamReader reader = processor.getReader();
while (reader.hasNext()) { // while not end of XML
int event = reader.next(); // read next event
if (event == XMLEvent.START_ELEMENT &&
"City".equals(reader.getLocalName())) {
System.out.println(reader.getElementText());
}
}
}
Чтобы постоянно не дублировать в программе часто повторяющийся код поиска нужного события в XML, мы можем добавить его в StaxStreamProcessor
:
public boolean doUntil(int stopEvent, String value) throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
if (event == stopEvent && value.equals(reader.getLocalName())) {
return true;
}
}
return false;
}
Пользоваться утильным классом станет не просто, а очень просто:
while (processor.doUntil(XMLEvent.START_ELEMENT, "City")){
System.out.println(reader.getElementText());
}
Недостаток этого кода — мы совершенно бесполезно потратим ресурсы на прохождение по сотням тысяч ненужных нам юзеров вместо завершения программы. Нужно добавить условие прекращение сканирования XML. Обычно это конец тэга родительского элемента (в нашем случае Cities
). Добавляем в StaxStreamProcessor
еще один утильный метод, который сканирует XML либо до конца тэга родителя, либо до заданного элемента:
public boolean startElement(String element, String parent) throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
if (parent != null && event == XMLEvent.END_ELEMENT &&
parent.equals(reader.getLocalName())) {
return false;
}
if (event == XMLEvent.START_ELEMENT &&
element.equals(reader.getLocalName())) {
return true;
}
}
return false;
}
Добавим методы чтения атрибута и текста:
public String getAttribute(String name) throws XMLStreamException {
return reader.getAttributeValue(null, name);
}
public String getText() throws XMLStreamException {
return reader.getElementText();
}
Код вызова останется суперпростым и мы прекратим обработку XML сразу после конца тега Cities
:
while (processor.startElement("City", "Cities")) {
System.out.println(processor.getAttribute("id") +":" + processor.getText());
}
StAX API требует аккуратность при чтении событий. Если в выводе мы помяняем местами чтение атрибута и текста, но код станет нерабочим: после прочтения из XML названия города атрибут останется позади и будет недоступен. Также следует помнить, что, в зависимости от текущего положения XML, нам доступны одни методы API чтения из XML и недоступны другие.
Используя startElement
можно добираться до элементов XML любой степени вложенности и, по мере необходимости, дополнять StaxStreamProcessor
другими утильными методами. Надеюсь что с данным подходом работа с StAX покажется вам легкой и удобной.
Спасибо за внимание и удачного кодинга!
Автор: gkislin
Источник [21]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/265238
Ссылки в тексте:
[1] StAX: https://en.wikipedia.org/wiki/StAX
[2] с элементами XML: http://genberm.narod.ru/xml/lections/xml/introduction.html
[3] JAXP (Java API for XML Processing): https://www.ibm.com/developerworks/ru/library/x-jaxp/
[4] Xerces: https://ru.wikipedia.org/wiki/Xerces
[5] Xalan: https://ru.wikipedia.org/wiki/Xalan
[6] DOM: https://ru.wikipedia.org/wiki/Document_Object_Model
[7] JAXB: https://ru.wikipedia.org/wiki/Java_Architecture_for_XML_Binding
[8] интерфейса org.w3c.dom.Node: https://docs.oracle.com/javase/8/docs/api/org/w3c/dom/Node.html
[9] DTD: https://ru.wikipedia.org/wiki/DTD
[10] XSD: https://ru.wikipedia.org/wiki/XML_Schema_(W3C)
[11] XPath: https://ru.wikipedia.org/wiki/XPath
[12] подсчитать их количество: https://stackoverflow.com/questions/7393541/count-of-element-in-xpath
[13] ESB: https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D0%B0%D1%8F_%D1%88%D0%B8%D0%BD%D0%B0_%D0%BF%D1%80%D0%B5%D0%B4%D0%BF%D1%80%D0%B8%D1%8F%D1%82%D0%B8%D1%8F
[14] XSL, XSLT: https://ru.wikipedia.org/wiki/XSL
[15] отдать с сервера в браузер ответ в виде XML и задать его XSL-преобразование в HTML: https://habrahabr.ru/post/43356/
[16] SAX: https://ru.wikipedia.org/wiki/SAX
[17] callback hell: http://callbackhell.ru/
[18] сравнительной табличке API по их возможностям: http://www.duct-tape-architect.ru/?p=315#i
[19] XMLStreamReader: https://www.ibm.com/developerworks/ru/library/x-stax1/
[20] XMLEventReader: https://www.ibm.com/developerworks/ru/library/x-stax2/
[21] Источник: https://habrahabr.ru/post/339716/
Нажмите здесь для печати.