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

Чтение и запись 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
Автор
Редакция

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

Готовьте пасту под плейлист Spotify
Кулинария

Готовьте пасту под плейлист Spotify

Визуальные рецепты: GIF и короткие видео
Рецепты

Визуальные рецепты: GIF и короткие видео

Как пользоваться голосовыми покупками Alexa
Инструкции

Как пользоваться голосовыми покупками Alexa

Лучшие приложения для обучения кулинарии на iPhone
Кулинария

Лучшие приложения для обучения кулинарии на iPhone

Как заморозить строки и столбцы в Google Sheets
Руководства

Как заморозить строки и столбцы в Google Sheets

SideChef: обзор и руководство по планированию еды
Планирование питания

SideChef: обзор и руководство по планированию еды