Java Language
Анализ XML с использованием API JAXP
Поиск…
замечания
XML Parsing - это интерпретация XML-документов, чтобы манипулировать их контентом с помощью разумных конструкций, будь то «узлы», «атрибуты», «документы», «пространства имен» или события, связанные с этими конструкциями.
Java имеет собственный API для обработки документов XML, называемый JAXP или Java API для обработки XML . JAXP и эталонная реализация были включены в каждую версию Java после Java 1.4 (JAXP v1.1) и с тех пор развиваются. Java 8 поставляется с JAXP версии 1.6.
API предоставляет различные способы взаимодействия с XML-документами, которые:
- Интерфейс DOM (Document Object Model)
- Интерфейс SAX (простой API для XML)
- Интерфейс StAX (Streaming API для XML)
Принципы интерфейса DOM
Интерфейс DOM предназначен для предоставления W3C DOM- совместимого способа интерпретации XML. Различные версии JAXP поддерживают различные спецификации DOM (до уровня 3).
В интерфейсе Document Object Model документ XML представлен как дерево, начиная с «Элемента документа». Базовый типом API является Node
типа, это позволяет перемещаться от Node
к его родителям, его детям, или его братьям (хотя, не все Node
с может иметь детей, например, Text
узлы являются окончательными в дереве, и никогда не имеют детей). Теги XML представлены как Element
s, которые значительно расширяют Node
с помощью связанных с атрибутами методов.
Интерфейс DOM очень полезен, поскольку он позволяет «одну строку» анализировать XML-документы как деревья и позволяет легко модифицировать построенное дерево (добавление узла, подавление, копирование и т. Д.) И, наконец, его сериализацию (обратно на диск ) после изменений. Это происходит по цене, однако: дерево находится в памяти, поэтому деревья DOM не всегда практичны для огромных XML-документов. Кроме того, построение дерева не всегда является самым быстрым способом работы с XML-контентом, особенно если его не интересуют все части документа XML.
Принципы интерфейса SAX
SAX API - это ориентированный на события API для работы с документами XML. В рамках этой модели компоненты XML-документов интерпретируются как события (например, «открыт тег», «тег закрыт», «встречен текстовый узел», «был встречен комментарий»). ..
API SAX использует подход «синтаксический разбор», где SAX Parser
отвечает за интерпретацию XML-документа и вызывает методы для делегата ( ContentHandler
) для обработки любого события, которое встречается в документе XML. Обычно один никогда не пишет парсер, но один обеспечивает обработчик для сбора всей необходимой информации из XML-документа.
Интерфейс SAX преодолевает ограничения интерфейса DOM, сохраняя только минимально необходимые данные на уровне анализатора (например, контексты пространств имен, состояние проверки), поэтому только информация, ContentHandler
для которой вы, разработчик, несет ответственность, - это хранится в памяти. Компромисс заключается в том, что при таком подходе нет возможности «вернуться во времени / XML-документ»: в то время как DOM позволяет Node
возвращаться к его родительскому объекту, такой возможности нет в SAX.
Принципы интерфейса StAX
API StAX использует аналогичный подход для обработки XML как API SAX (т. Е. Управляемый событиями), единственное очень существенное отличие состоит в том, что StAX является синтаксическим анализатором (где SAX был парсером push). В SAX Parser
находится под контролем и использует обратные вызовы в ContentHandler
. В Stax вы вызываете парсер и управляете, когда / если вы хотите получить следующее «событие» XML.
API начинается с XMLStreamReader (или XMLEventReader ), которые являются шлюзами, через которые разработчик может спросить nextEvent()
в стиле итератора.
Анализ и перемещение документа с использованием DOM API
Учитывая следующий документ:
<?xml version='1.0' encoding='UTF-8' ?>
<library>
<book id='1'>Effective Java</book>
<book id='2'>Java Concurrency In Practice</book>
</library>
Для построения дерева DOM из String
можно использовать следующий код:
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
public class DOMDemo {
public static void main(String[] args) throws Exception {
String xmlDocument = "<?xml version='1.0' encoding='UTF-8' ?>"
+ "<library>"
+ "<book id='1'>Effective Java</book>"
+ "<book id='2'>Java Concurrency In Practice</book>"
+ "</library>";
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// This is useless here, because the XML does not have namespaces, but this option is usefull to know in cas
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// There are various options here, to read from an InputStream, from a file, ...
Document document = documentBuilder.parse(new InputSource(new StringReader(xmlDocument)));
// Root of the document
System.out.println("Root of the XML Document: " + document.getDocumentElement().getLocalName());
// Iterate the contents
NodeList firstLevelChildren = document.getDocumentElement().getChildNodes();
for (int i = 0; i < firstLevelChildren.getLength(); i++) {
Node item = firstLevelChildren.item(i);
System.out.println("First level child found, XML tag name is: " + item.getLocalName());
System.out.println("\tid attribute of this tag is : " + item.getAttributes().getNamedItem("id").getTextContent());
}
// Another way would have been
NodeList allBooks = document.getDocumentElement().getElementsByTagName("book");
}
}
Код дает следующее:
Root of the XML Document: library
First level child found, XML tag name is: book
id attribute of this tag is : 1
First level child found, XML tag name is: book
id attribute of this tag is : 2
Разбор документа с использованием API StAX
Учитывая следующий документ:
<?xml version='1.0' encoding='UTF-8' ?>
<library>
<book id='1'>Effective Java</book>
<book id='2'>Java Concurrency In Practice</book>
<notABook id='3'>This is not a book element</notABook>
</library>
Можно использовать следующий код для его анализа и построения карты названий книг по идентификатору книги.
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
public class StaxDemo {
public static void main(String[] args) throws Exception {
String xmlDocument = "<?xml version='1.0' encoding='UTF-8' ?>"
+ "<library>"
+ "<book id='1'>Effective Java</book>"
+ "<book id='2'>Java Concurrency In Practice</book>"
+ "<notABook id='3'>This is not a book element </notABook>"
+ "</library>";
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
// Various flavors are possible, e.g. from an InputStream, a Source, ...
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xmlDocument));
Map<Integer, String> bookTitlesById = new HashMap<>();
// We go through each event using a loop
while (xmlStreamReader.hasNext()) {
switch (xmlStreamReader.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
System.out.println("Found start of element: " + xmlStreamReader.getLocalName());
// Check if we are at the start of a <book> element
if ("book".equals(xmlStreamReader.getLocalName())) {
int bookId = Integer.parseInt(xmlStreamReader.getAttributeValue("", "id"));
String bookTitle = xmlStreamReader.getElementText();
bookTitlesById.put(bookId, bookTitle);
}
break;
// A bunch of other things are possible : comments, processing instructions, Whitespace...
default:
break;
}
xmlStreamReader.next();
}
System.out.println(bookTitlesById);
}
Эти результаты:
Found start of element: library
Found start of element: book
Found start of element: book
Found start of element: notABook
{1=Effective Java, 2=Java Concurrency In Practice}
В этом примере нужно позаботиться о нескольких вещах:
Использование
xmlStreamReader.getAttributeValue
работает, потому что мы сначала отметили, что парсер находится в состоянииSTART_ELEMENT
. В других состояниях (кромеATTRIBUTES
) парсеру порученоIllegalStateException
, поскольку атрибуты могут появляться только в начале элементов.xmlStreamReader.getTextContent()
же самое касаетсяxmlStreamReader.getTextContent()
, он работает, потому что мы находимся вSTART_ELEMENT
и мы знаем в этом документе, что элемент<book>
не имеет дочерних узлов, не содержащих текст.
Для более сложных анализов документов (более глубокие, вложенные элементы, ...), хорошая практика «делегировать» парсер под-методам или другим объектам, например, иметь класс или метод BookParser
и иметь дело с каждым элементом от START_ELEMENT до END_ELEMENT книги XML-тега.
Можно также использовать объект Stack
для хранения важных данных вверх и вниз по дереву.