Гид по технологиям

Чтение и запись XML в Java с помощью DOM и Transform API

7 min read Java XML Обновлено 01 Jan 2026
Чтение и запись XML в Java
Чтение и запись XML в Java

Схема чтения и записи XML

XML-файлы используются для хранения и передачи структурированных данных. До популяризации JSON XML часто был основным форматом для представления данных. В современных проектах XML встречается реже, но навык работы с ним по-прежнему полезен: например, при интеграции с устаревшими сервисами, конфигурационными файлами или обмене данными с внешними системами.

Важно: под «XML» мы подразумеваем стандартный текстовый формат с элементами, атрибутами и иерархией. DOM — модель документа, которая загружает весь файл в память; SAX и StAX — альтернативы для других сценариев.

Что вам понадобится для работы с XML в Java

Java SE включает Java API for XML Processing (JAXP) — общий набор API для обработки XML. Основные подходы внутри JAXP:

  • DOM — Document Object Model: удобная объектная модель (Element, Node, Attr). Загружает весь документ в память, поэтому плохо подходит для очень больших XML.
  • SAX — Simple API for XML: событийно-ориентированный парсер. Небольшой объём памяти, но код сложнее — вы обрабатываете события начала/конца элементов и текста.
  • StAX — Streaming API for XML: потоковый, «pull»-парсер. Компромисс между простотой и экономией памяти — удобнее, чем SAX, и эффективнее, чем DOM для больших потоков.

Для работы с DOM и трансформацией вам понадобятся стандартные пакеты (пример импорта приведён в коде ниже):

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;

Подготовка примерного XML-файла

Пример XML-файла (выдержка)

Для демонстрации возьмём фрагмент типичного файла каталога книг (пример из Microsoft). Ключевые элементы: корневой элемент и повторяющиеся элементы с атрибутом id и вложенными элементами author, title, genre, price, publish_date, description.

Пример (фрагмент):



  
    Gambardella, Matthew
    XML Developer's Guide
    Computer
    44.95
    2000-10-01
    An in-depth look at creating applications
      with XML.
  
  
    Ralls, Kim
    ...

Формат даты в примере — ISO-подобный yyyy-MM-dd.

Чтение XML через DOM API — базовые шаги

Общий подход при DOM: создать фабрику DocumentBuilderFactory, затем DocumentBuilder и распарсить файл в объект Document — корневой узел будет доступен через getDocumentElement().

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();

// XML-файл для чтения
File file = new File("");
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();

После этого весь XML доступен в памяти, и вы можете навигировать по дереву через методы DOM.

Извлечение данных через DOM API

Ниже — типичная операция: получить список дочерних элементов и пройтись по ним. Обратите внимание: getChildNodes() возвращает также текстовые узлы и комментарии, поэтому при обходе нужно фильтровать по типу узла.

NodeList books = catalog.getChildNodes();

for (int i = 0, ii = 0, n = books.getLength(); i < n; i++) {
  Node child = books.item(i);

  if (child.getNodeType() != Node.ELEMENT_NODE)
    continue;

  Element book = (Element) child;
  // работа с элементом book
}

Чтобы упростить поиск вложенных элементов по имени, удобно добавить утилитные методы:

static private Node findFirstNamedElement(Node parent, String tagName) {
  NodeList children = parent.getChildNodes();

  for (int i = 0, in = children.getLength(); i < in; i++) {
    Node child = children.item(i);

    if (child.getNodeType() != Node.ELEMENT_NODE)
      continue;

    if (child.getNodeName().equals(tagName))
      return child;
  }

  return null;
}

И ещё один удобный метод: получение текстового содержимого элемента. В DOM текст может быть представлен несколькими соседними TEXT_NODE, поэтому нужно собрать их.

static private String getCharacterData(Node parent) {
  StringBuilder text = new StringBuilder();

  if (parent == null)
    return text.toString();

  NodeList children = parent.getChildNodes();

  for (int k = 0, kn = children.getLength(); k < kn; k++) {
    Node child = children.item(k);

    if (child.getNodeType() != Node.TEXT_NODE)
      break;

    text.append(child.getNodeValue());
  }

  return text.toString();
}

С этими вспомогательными функциями код для вывода информации по каждому выглядит так:

NodeList books = catalog.getChildNodes();

for (int i = 0, ii = 0, n = books.getLength(); i < n; i++) {
  Node child = books.item(i);

  if (child.getNodeType() != Node.ELEMENT_NODE)
    continue;

  Element book = (Element) child;
  ii++;

  String id = book.getAttribute("id");
  String author = getCharacterData(findFirstNamedElement(child, "author"));
  String title = getCharacterData(findFirstNamedElement(child, "title"));
  String genre = getCharacterData(findFirstNamedElement(child, "genre"));
  String price = getCharacterData(findFirstNamedElement(child, "price"));
  String pubdate = getCharacterData(findFirstNamedElement(child, "pubdate"));
  String descr = getCharacterData(findFirstNamedElement(child, "description"));

  System.out.printf("%3d. book id = %s\n"
    + " author: %s\n"
    + " title: %s\n"
    + " genre: %s\n"
    + " price: %s\n"
    + " pubdate: %s\n"
    + " descr: %s\n",
    ii, id, author, title, genre, price, pubdate, descr);
}

Пояснения к коду:

  • Итерация по дочерним узлам корневого элемента catalog.
  • Пропускаются узлы, которые не являются элементами (TEXT_NODE, комментарии и т. п.).
  • Приведение (Element) child даёт доступ к методам Element.
  • Для получения текстовых данных используется getCharacterData.

Пример вывода показан на рисунке ниже:

Пример вывода парсинга XML в Java

Запись XML с помощью Transform API

Java предоставляет API преобразований XML (Transformer API). Частый сценарий — использовать «identity»-трансформ (то есть без XSLT), чтобы записать объект Document обратно в поток или файл. Разберём пример: добавление нового элемента в каталог.

Исходные данные о новой книге можно хранить в properties-файле или получить из базы. Пример properties (фрагмент):

id=bk113
author=Jane Austen
title=Pride and Prejudice
genre=Romance
price=6.99
publish_date=2010-04-01
description=It is a truth universally acknowledged, ...

Сначала парсим существующий XML (как в разделе выше):

File file = new File("..."); // XML-файл
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();

Затем загружаем properties:

String propsFile = "";
Properties props = new Properties();

try (FileReader in = new FileReader(propsFile)) {
  props.load(in);
}

Достаём значения:

String id = props.getProperty("id");
String author = props.getProperty("author");
String title = props.getProperty("title");
String genre = props.getProperty("genre");
String price = props.getProperty("price");
String publish_date = props.getProperty("publish_date");
String descr = props.getProperty("description");

Создаём элемент и присваиваем атрибут id:

Element book = document.createElement("book");
book.setAttribute("id", id);

Добавление дочерних элементов простое; для удобства можно пройти по списку имён:

List elnames = Arrays.asList("author", "title", "genre", "price",
  "publish_date", "description");

for (String elname : elnames) {
  Element el = document.createElement(elname);
  Text text = document.createTextNode(props.getProperty(elname));
  el.appendChild(text);
  book.appendChild(el);
}

catalog.appendChild(book);

Осталось записать изменённый документ наружу. Создаём Transformer и настраиваем отступы:

TransformerFactory tfact = TransformerFactory.newInstance();
Transformer tform = tfact.newTransformer();
tform.setOutputProperty(OutputKeys.INDENT, "yes");
tform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");

// вывод в консоль
tform.transform(new DOMSource(document), new StreamResult(System.out));

// или запись в файл
// tform.transform(new DOMSource(document), new StreamResult(new File("output.xml")));

Готово — вы прочитали XML, модифицировали DOM и записали результат.

Когда DOM подходит, а когда нет — сравнение DOM / SAX / StAX

Краткая матрица выбора:

КритерийDOMSAXStAX
ПамятьЗагружает весь документ (высокая)НизкаяНизкая / средняя
Простота чтенияОчень удобно (объектная модель)Сложнее (обработчики событий)Удобнее, чем SAX (pull API)
Редактирование дереваПрямо в памятиНет (только обработка)Частично (построчно/поточно)
Подходит дляНебольших/средних документов, когда нужно менять структуруБольших потоков, парсинг без измененийПотоковая обработка со средней сложностью

Decision flow (поможет выбрать подход):

flowchart TD
  A[Нужен доступ ко всему дереву?] -->|Да| B[Используйте DOM]
  A -->|Нет| C[Нажатие на объём данных]
  C -->|Большой| D[Используйте SAX или StAX]
  C -->|Средний| E[StAX — баланс удобства и памяти]
  D --> F[Если нужен event-driven — SAX, если prefer pull — StAX]

Важно: если планируется модификация структуры (добавление/удаление узлов), DOM часто удобнее, но требует памяти. Для сквозной обработки больших файлов — используйте SAX/StAX.

Альтернативные подходы и интеграции

  • Использовать сторонние библиотеки (например, JAXB для биндинга XML в Java-объекты) — удобно, когда у вас фиксированная схема и много кода по маппингу. JAXB генерирует классы из XSD и упрощает серилизацию/десериализацию.
  • Если требуется многократно выполнять сложные преобразования, стоит рассмотреть XSLT или XQuery.
  • Для больших данных рассмотреть парсеры на основе pull-методов (StAX) или стриминговую обработку со специализированными библиотеками.

Примеры не подходят, если вам нужен строго валидированный ввод по сложной XSD-схеме — в этом случае добавьте валидацию при разборе (DocumentBuilderFactory.setSchema(…)).

Практические советы и эвристики

  • Ментальная модель DOM: представьте XML как дерево объектов — каждый элемент это узел с детьми и атрибутами. Операции чтения/изменения — обычные манипуляции с деревом.
  • При чтении используйте фильтрацию узлов по типу: ELEMENT_NODE — элементы, TEXT_NODE — текст.
  • Всегда закрывайте потоки и используйте try-with-resources для input/output.
  • Для точности парсинга дат и чисел используйте явную валидацию/парсинг (например, BigDecimal для цен).

Чек-лист ролей

Разработчик:

  • Выбрал подходящий API (DOM / SAX / StAX / JAXB)
  • Обработаны все сущности XML с фильтрацией текстовых узлов
  • Испытаны граничные случаи (пустые теги, отсутствующие атрибуты)
  • Добавлены юнит-тесты для парсера

Сопровождающий / DevOps:

  • Проверить, что большие файлы не приводят к OOM
  • Настроить мониторинг на время обработки и использование памяти
  • Обеспечить резервные копии исходных XML перед модификацией

Тестировщик:

  • Тестовые файлы с некорректной структурой (сломанный XML)
  • Тестовые файлы с отсутствующими полями
  • Тесты на сохранение/восстановление данных после трансформации

Чек-лист приёмки

Критерии приёмки:

  • Программа корректно читает валидный XML и извлекает требуемые поля.
  • Программа корректно добавляет новый элемент и сохраняет файл в формате XML.
  • Изменённый файл соответствует базовым требованиям к форматированию (отступы читаемы) и остаётся валидным XML.
  • Приложение устойчиво к пустым/неожидаемым элементам и не падает с NPE.

Маленькая шпаргалка (cheat sheet)

  • Импорты: javax.xml.parsers., javax.xml.transform., org.w3c.dom.*
  • Создать парсер: DocumentBuilderFactory.newInstance() -> newDocumentBuilder()
  • Прочитать: builder.parse(new File(“file.xml”))
  • Создать элемент: document.createElement(“name”)
  • Добавить текст: document.createTextNode(“text”)
  • Трансформер: TransformerFactory.newInstance().newTransformer()
  • Включить отступы: setOutputProperty(OutputKeys.INDENT, “yes”)
  • Записать: tform.transform(new DOMSource(document), new StreamResult(new File(“out.xml”)))

1-строчная глоссарий

  • DOM — объектная модель документа (Document Object Model).
  • SAX — событийный парсер XML.
  • StAX — потоковый парсер с pull-интерфейсом.
  • Transformer — API для преобразования/записи XML.

Частые ошибки и как их избежать

  • Ошибка: чтение через getChildNodes() и прямое приведение без фильтра по типу — приводит к ClassCastException. Решение: проверять node.getNodeType() == Node.ELEMENT_NODE.
  • Ошибка: ожидание, что текст всегда один TEXT_NODE. Решение: собрать все последовательные TEXT_NODE как в getCharacterData.
  • Ошибка: использование DOM для многогигабайтных файлов — OOM. Решение: использовать StAX или SAX.

Пример сценариев тестирования

  • Тест 1: Чтение каталога из маленького файла — ожидается корректный список книг.
  • Тест 2: Добавление книги и сохранение — новый элемент должен присутствовать в output.xml.
  • Тест 3: Некорректный XML (ломаная разметка) — парсер должен бросить исключение и не потерять исходный файл.

Итог

Работа с XML в Java — это набор простых приёмов: выбрать подходящий API, корректно парсить узлы и текст, не забывать о проверках и тестах. DOM удобен для редактирования и небольших документов; SAX и StAX — для потоковой обработки и больших объёмов.

Важно: выбирайте инструмент под задачу, добавляйте проверку границ и юнит-тесты, и тогда интеграция с XML будет надёжной и поддерживаемой.

Примечание: при необходимости автоматического маппинга между XML и Java-классами рассмотрите JAXB как альтернативу ручному обходу дерева.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство