Author

Java Read XML with StAX Parser – Cursor and Iterator APIs

Learn to parse and read XML files using Java StAX parser. StAX (Streaming API for XML) provides two ways to parse XML:

This tutorial will discuss both APIs for parsing an XML file.

1. Introduction to StAX Parser

Just like SAX parser, StAX API is designed for parsing XML streams. The difference is:

  • StAX is a “ pull ” API. SAX is a “ push ” API.
  • StAX can do both XML reading and writing. SAX can only do XML reading.

StAX is a pull-style API. This means that we have to move the StAX parser from item to item in the XML file ourself, just like we do with a standard Iterator or JDBC ResultSet . We can then access the XML information via the StAX parser for each such “item” encountered in the XML file.

2. Difference between Cursor and Iterator APIs

While reading the XML document, the iterator reader returns an XML event object from its nextEvent() calls. This event provides information about what type of XML tag (element, text, comment etc) we have encountered. The event received is immutable so we can pass around the application to process it safely.

XMLEventReader reader = . ; while(reader.hasNext()) < XMLEvent event = reader.nextEvent(); if(event.getEventType() == XMLEvent.START_ELEMENT)< //process data >//. more event types handled here. >

Unlike Iterator, the cursor works like Resultset in JDBC. If the cursor moves to the next element in the XML document. You can then call methods directly on the cursor to obtain more information about the current event.

XMLStreamReader streamReader = . ; while(streamReader.hasNext()) < int eventType = streamReader.next(); if(eventType == XMLStreamReader.START_ELEMENT)< System.out.println(streamReader.getLocalName()); >//. more event types handled here. >

Given below demonstrate how to use StAX iterator-based API to read the XML document to an object.

The XML file is as follows:

  Lokesh Gupta   Brian Lara   

To read the file, I have written the program in these steps:

  • Create an iterator and start receiving events.
  • As soon as you get open ’employee’ tag – create new Employee object.
  • Read id attribute from employee tag and set it to current Employee object.
  • Iterate to the next start tag events. These are XML elements inside employee tag. Read data inside these tags. Set read data to current Employee object.
  • Continue iterating the event. When you find the end element event for ’employee’ tag, you can say that you have read the data for the current employee , so add the current employee object to employeeList collection.
  • At last, verify the read data by printing the employeeList .
import com.howtodoinjava.xml.model.Employee; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.Characters; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; public class ReadXmlWithIterator < public static void main(String[] args) throws FileNotFoundException, XMLStreamException < File file = new File("employees.xml"); // Instance of the class which helps on reading tags XMLInputFactory factory = XMLInputFactory.newInstance(); // Initializing the handler to access the tags in the XML file XMLEventReader eventReader = factory.createXMLEventReader(new FileReader(file)); //All read employees objects will be added to this list ListemployeeList = new ArrayList<>(); //Create Employee object. It will get all the data using setter methods. //And at last, it will stored in above 'employeeList' Employee employee = null; // Checking the availability of the next tag while (eventReader.hasNext()) < XMLEvent xmlEvent = eventReader.nextEvent(); if (xmlEvent.isStartElement()) < StartElement startElement = xmlEvent.asStartElement(); //As soo as employee tag is opened, create new Employee object if ("employee".equalsIgnoreCase(startElement.getName().getLocalPart())) < employee = new Employee(); >//Read all attributes when start tag is being read @SuppressWarnings("unchecked") Iterator iterator = startElement.getAttributes(); while (iterator.hasNext()) < Attribute attribute = iterator.next(); QName name = attribute.getName(); if ("id".equalsIgnoreCase(name.getLocalPart())) < employee.setId(Integer.valueOf(attribute.getValue())); >> //Now everytime content tags are found; //Move the iterator and read data switch (startElement.getName().getLocalPart()) < case "name": Characters nameDataEvent = (Characters) eventReader.nextEvent(); employee.setName(nameDataEvent.getData()); break; case "title": Characters titleDataEvent = (Characters) eventReader.nextEvent(); employee.setTitle(titleDataEvent.getData()); break; >> if (xmlEvent.isEndElement()) < EndElement endElement = xmlEvent.asEndElement(); //If employee tag is closed then add the employee object to list; //and be ready to read next employee data if ("employee".equalsIgnoreCase(endElement.getName().getLocalPart())) < employeeList.add(employee); >> > System.out.println(employeeList); //Verify read data > >
[Employee [id=101, name=Lokesh Gupta, title=Author], Employee [id=102, name=Brian Lara, title=Cricketer]]

I will read the same employees.xml file – now with cursor-based API.

import com.howtodoinjava.xml.model.Employee; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.ArrayList; import java.util.List; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; public class ReadXmlWithCursor < public static void main(String[] args) throws FileNotFoundException, XMLStreamException < //All read employees objects will be added to this list ListemployeeList = new ArrayList<>(); //Create Employee object. It will get all the data using setter methods. //And at last, it will stored in above 'employeeList' Employee employee = null; File file = new File("employees.xml"); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader streamReader = factory.createXMLStreamReader(new FileReader(file)); while (streamReader.hasNext()) < //Move to next event streamReader.next(); //Check if its 'START_ELEMENT' if (streamReader.getEventType() == XMLStreamReader.START_ELEMENT) < //employee tag - opened if (streamReader.getLocalName().equalsIgnoreCase("employee")) < //Create new employee object asap tag is open employee = new Employee(); //Read attributes within employee tag if (streamReader.getAttributeCount() >0) < String "id"); employee.setId(Integer.valueOf(id)); >> //Read name data if (streamReader.getLocalName().equalsIgnoreCase("name")) < employee.setFirstName(streamReader.getElementText()); >//Read title data if (streamReader.getLocalName().equalsIgnoreCase("title")) < employee.setLastName(streamReader.getElementText()); >> //If employee tag is closed then add the employee object to list if (streamReader.getEventType() == XMLStreamReader.END_ELEMENT) < if (streamReader.getLocalName().equalsIgnoreCase("employee")) < employeeList.add(employee); >> > //Verify read data System.out.println(employeeList); > >

So in this StAX parser tutorial, we learned the following things:

  1. What is StAX parser based on XML streaming API?
  2. Difference between StAX vs SAX parsers.
  3. How to read XML with StAX iterator API with example.
  4. How to read XML with StAX cursor API with example.

Both APIs are capable of parsing any kind of XML document but the cursor API is more memory-efficient than the iterator API. So, if your application needs better performance, consider using the cursor-based API.

Drop me your questions in the comments section.

Источник

Java XML API: выбираем правильно. StAX: работаем с удовольствием

Здравствуйте!
Несмотря на снижение популярности формата XML с начала 2000х, он прочно занял свои ниши. Я сталкивался с обработкой XML ~ в 60% проектов и посвятил ей занятие своей стажировки Masterjava. Наиболее частые его применения: XHTML, SOAP, различные конфигурации (например Tomcat, SoapUI, IntelliJ IDEA, Spring XML конфигурация), импорт-экспорт данных.

В Java есть несколько API для работы с XML и для разработчика важно понимать, какое из API требуется выбрать в каждой конкретной ситуации. В этой статье я кратко перечислю все Java XML API, их назначение и примеры использования, и подробнее остановлюсь на работе с достаточно редкой, но в ряде случаев единственно верной технологией StAX. Предполагается что с элементами XML вы уже знакомы.

Java XML API: выбираем правильно

  • JAXP (Java API for XML Processing) это набор API (SAX + DOM + валидация DTD + XSLT). Xerces и Xalan — стандартные реализации этих API. Кроме XSLT все API устарели: на смену SAX пришел StAX, DOM сменил JAXB, DTD заменил XSD.
  • DOM и JAXB — API для полного зачитывания XML и получения в приложении его готового представления в объектах Java. Для DOM это коллекции реализаций интерфейса org.w3c.dom.Node (атрибуты, элементы, текст, ..). Для JAXB задается мапинг, наподобие тому, как в ORM задаются отображения таблиц базы данных в объекты Java. Т.е. из XML получаются готовые и удобные в использовании Java бины. JAXB — наиболее удобный и часто используемый API для работы с XML, когда требуется почитать весь XML (он должен поместиться в память JVM) и выполнить с ним требуемые действия.
  • DTD (устаревший) и XSD — схемы, задающие проверку структуры XML (порядок следования элементов, обязательность или опциональность элемента, наличие атрибутов у элемента). К XML можно привязать схему проверки структуры, и тогда инструменты, работающие с XML (например IntelliJ IDEA) могут делать автодополнение и показывать ошибки схемы. Также можно провалидировать XML документ из приложения. Формат XSD более современный и сам является XML документом.
  • XPath язык запросов к XML. Грубо можно сравнить с запросом SQL к базе данных, кода нам нужно достать из XML какой-то определенный элемент (или элементы) или подсчитать их количество. Я видел их применение, например, в ESB: данные между системами передаются в XML формате и в конфигурации каждая система через XPath «выкусывает» из XML нужные ей данные. Чаще всего XPath используется при трансформации XML.
  • XSL, XSLT — преобразования XML в любой другой формат. Одно из применений, например, отдать с сервера в браузер ответ в виде XML и задать его XSL-преобразование в HTML. Все современные браузеры поддерживают XSLT преобразования, т.е. на стороне клиента самостоятельно трансформируют XML по заданной XSL к требуемому виду.
  • SAX и StAX — последовательное чтение из источника XML.Документ читается последовательно по кусочкам (событиям). API между собой отличаются тем, что SAX основан на модели push, а Stax на модели pull. Если вы когда-то делали на javascript несколько последовательных AJAX запросов, то сталкивались с callback hell. Выполнение кода делается асинхронно и код пишется не последовательно, а ступеньками в функциях возврата. При использовании SAX происходит нечто подобное: нужно задавать функции для обработки определенных событий в XML (начало тэга, конец тэга, текст внутри тэга, комментарий) и внутри этих обработчиков задавать новые обработчики. Работать со StAX гораздо удобнее. Мы последовательно читаем из документа события, анализируем их и обрабатываем подходящие. Эти API используются при очень больших документах, из которых зачитываются списки объектов по одному по мере их появления в документе, либо когда нам интересен не весь документ, а его определенная часть и модель всего документа не нужна.

StAX: работаем с удовольствием

Прежде всего хочу отметить, что со StAX можно работать через 2 API: низкоуровневое XMLStreamReader, возвращающая примитивы и высокоуровневое XMLEventReader, которое возвращает объекты и расходует больше памяти. Далее я буду работать с XMLStreamReader. Сделав над ним обертку работа с XML будет простой и удобной.Разберем небольшой пример: есть простой XML с городами и юзерами:

   Санкт-Петербург Москва . .  gmail@gmail.com Gmail User  admin@javaops.ru Admin  . . 

В реальности этот 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"))

Недостаток этого кода — мы совершенно бесполезно потратим ресурсы на прохождение по сотням тысяч ненужных нам юзеров вместо завершения программы. Нужно добавить условие прекращение сканирования 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
while (processor.startElement("City", "Cities"))

StAX API требует аккуратность при чтении событий. Если в выводе мы помяняем местами чтение атрибута и текста, но код станет нерабочим: после прочтения из XML названия города атрибут останется позади и будет недоступен. Также следует помнить, что, в зависимости от текущего положения XML, нам доступны одни методы API чтения из XML и недоступны другие.Используя startElement можно добираться до элементов XML любой степени вложенности и, по мере необходимости, дополнять StaxStreamProcessor другими утильными методами. Надеюсь что с данным подходом работа с StAX покажется вам легкой и удобной.

Спасибо за внимание и приятного кодинга!

Источник

Читайте также:  Python google cloud install
Оцените статью