W3docs

Введение в Java XML

Обзор обработки XML в Java — подходы DOM, SAX, StAX и JAXB с примерами.

XML (Extensible Markup Language) — это текстовый формат для представления структурированных иерархических данных с помощью вложенных тегов. Задолго до того, как JSON стал доминировать в веб-API, XML был стандартным форматом для конфигурационных файлов, форматов документов и обмена сообщениями — и он по-прежнему повсюду: от файлов Maven pom.xml до SOAP-сервисов и документов Office.

Java обладает богатой встроенной поддержкой XML в JDK: вам не нужны внешние библиотеки для чтения или записи XML. Пакеты javax.xml.parsers и org.w3c.dom, а также org.xml.sax и javax.xml.stream предоставляют три различные модели разбора. Эта глава описывает, что такое XML, какую модель разбора выбрать и чем XML отличается от JSON — чтобы остальные главы этой части (DOM, SAX и JAXB) строились на чёткой основе.

На этой странице рассматривается:

  • Как выглядит XML-документ и основные термины (элемент, атрибут, корень, корректно сформированный).
  • Три модели разбора JDK — DOM, SAX и StAX — и когда каждая из них подходит.
  • Работающий пример DOM с использованием только классов JDK.
  • Чем XML и JSON отличаются друг от друга, чтобы вы могли выбрать подходящий формат.

Как выглядит XML

XML-документ — это дерево элементов. Каждый элемент имеет имя, необязательные атрибуты и либо текстовое содержимое, либо вложенные дочерние элементы. Всегда существует ровно один корневой элемент, оборачивающий всё остальное.

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <book id="1" lang="en">
    <title>Effective Java</title>
    <price>45.00</price>
  </book>
</catalog>

Здесь <catalog> — корневой элемент, <book> — дочерний элемент с двумя атрибутами (id и lang), а <title> и <price> содержат текст. XML-объявление в первой строке указывает версию и кодировку символов. Корректно сформированный XML требует, чтобы каждый открывающий тег был закрыт и правильно вложен.

Три модели разбора

JDK предлагает три способа чтения XML, каждый с разным компромиссом между удобством и памятью. Выбор правильной модели — самое важное решение при работе с XML.

МодельСтильПамятьЛучше всего для
DOMЗагружает всё дерево в памятьВысокаяПроизвольный доступ, редактирование, небольшие/средние документы
SAXPush-события при сканировании (обратные вызовы)НизкаяБольшие документы, потоковое чтение
StAXPull-события по требованию (курсор)НизкаяБольшие документы, с более простым потоком управления

DOM строит полное дерево в памяти, по которому можно свободно перемещаться и которое можно изменять. SAX вызывает обратные вызовы (startElement, characters, endElement) по мере чтения, никогда не удерживая весь документ. StAX также является потоковым, но позволяет вашему коду извлекать следующее событие по готовности, что обычно проще в понимании, чем обратные вызовы SAX.

DOM: дерево в памяти

DOM — наиболее удобная модель, когда документы достаточно малы, чтобы поместиться в памяти. Вы разбираете документ один раз, затем обходите или запрашиваете дерево столько раз, сколько нужно.

import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = factory.newDocumentBuilder().parse("catalog.xml");

NodeList books = doc.getElementsByTagName("book");
System.out.println("Books: " + books.getLength());

getElementsByTagName возвращает живой NodeList; вы индексируете его и приводите узлы к Element для чтения атрибутов и дочернего текста. В специальной главе Java XML DOM parser подробно рассматривается навигация, изменение и запись дерева.

Внимание

По умолчанию парсер JDK разрешает внешние сущности, что подвергает вас атакам XXE (XML External Entity) при разборе ненадёжных входных данных. Для промышленного кода, читающего XML из внешних источников, отключите DTD с помощью factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); перед созданием строителя.

SAX и StAX: потоковая обработка

Когда документ слишком велик, чтобы уместиться в памяти, его обрабатывают потоково. SAX передаёт события обработчику, который вы предоставляете:

import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;

DefaultHandler handler = new DefaultHandler() {
    public void startElement(String uri, String local, String name, Attributes a) {
        System.out.println("Start: " + name);
    }
};
SAXParserFactory.newInstance().newSAXParser()
    .parse("catalog.xml", handler);

StAX предоставляет курсор, которым вы управляете самостоятельно, что многие находят более понятным:

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamConstants;
import java.io.FileReader;

XMLStreamReader r = XMLInputFactory.newInstance()
    .createXMLStreamReader(new FileReader("catalog.xml"));
while (r.hasNext()) {
    if (r.next() == XMLStreamConstants.START_ELEMENT) {
        System.out.println("Start: " + r.getLocalName());
    }
}

Смотрите главу Java XML SAX parser для полного разбора обработчиков событий. Если вы предпочитаете не разбирать узлы вручную и хотите напрямую отображать XML на Java-объекты, JAXB связывает элементы с аннотированными классами.

Самодостаточный пример

Приведённый ниже работающий пример использует только классы JDK — никаких Jackson или JAXB не требуется. Он разбирает XML-каталог из строки в памяти с помощью DOM, обходит элементы <book>, читает атрибуты и дочерний текст, а также суммирует цены.

java— editable, runs on the server

Что можно вынести из запуска:

  • Разбор DOM не требует внешних зависимостей — DocumentBuilderFactory и org.w3c.dom входят в JDK, поэтому программа выводит результаты без каких-либо библиотек в classpath.
  • Имя корневого элемента, выведенное как catalog, подтверждает, что существует ровно один корень, оборачивающий весь документ.
  • getElementsByTagName("book") вернул NodeList длиной 2, поэтому вы индексируете его как список и приводите каждый элемент к Element.
  • Атрибуты (id) читаются с помощью getAttribute, а текстовое содержимое (title, price) — с помощью getTextContent: это разные виды данных одного элемента.
  • Поскольку всё дерево находится в памяти, суммирование цен по всем книгам до $83.50 — это просто цикл с произвольным доступом, что и делает DOM оправданным несмотря на расход памяти.

XML или JSON?

XML и JSON решают одну задачу — обмен структурированными данными — но делают разные компромиссы.

АспектXMLJSON
СинтаксисМногословные теги, открывающие и закрывающиеКомпактные фигурные и квадратные скобки
АтрибутыДа (id="1")Нет — всё является парой ключ/значение
КомментарииПоддерживаются (<!-- ... -->)Не поддерживаются
Схема/валидацияЗрелая (XSD, DTD)JSON Schema, менее распространена
Поддержка JDKВстроенная (javax.xml.*)Не встроена — нужна библиотека
Типичное применение сегодняКонфигурация, документы, SOAP, устаревшие системыВеб/REST API, современные сервисы

Используйте XML, когда нужно работать с существующим XML-форматом (SOAP-сервис, pom.xml, документ Office) или когда вам нужны атрибуты, комментарии или строгая валидация схемы. Используйте JSON для новых веб-API, где его меньший размер и нативная поддержка браузером дают преимущество.

Следующие шаги

  • Java XML DOM parser — чтение, изменение и запись дерева.
  • Java XML SAX parser — потоковая обработка больших документов с обработчиками событий.
  • JAXB — отображение XML на аннотированные Java-объекты и обратно.
  • Введение в Java JSON — современная альтернатива для веб-данных.

Практика

Практика
Какая модель разбора XML загружает весь документ в память в виде навигируемого дерева?
Какая модель разбора XML загружает весь документ в память в виде навигируемого дерева?
Was this page helpful?