JavaRush /Курсы /JAVA 25 SELF /Работа с DOM, SAX: парсинг XML

Работа с DOM, SAX: парсинг XML

JAVA 25 SELF
47 уровень, 1 лекция
Открыта

1. DOM (Document Object Model)

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

В таких ситуациях подходят старые и проверенные инструменты — DOM и SAX. Они позволяют напрямую работать с содержимым XML без привязки к заранее описанным Java-классам.

Как работает DOM

DOM — это способ представить XML-документ в виде дерева объектов в памяти. Каждый тег становится узлом дерева (Node), а атрибуты, текстовые значения и даже комментарии — отдельными объектами. После загрузки документа вы получаете полный доступ к его структуре: можно читать, изменять, удалять, добавлять элементы и атрибуты.

Основные классы DOM в Java

  • DocumentBuilderFactory — фабрика для создания парсера.
  • DocumentBuilder — парсер, который превращает XML в дерево.
  • Document — корневой объект дерева.
  • Element — элемент XML (тег).
  • NodeList — список узлов (например, все <item> внутри <items>).

Пример: Чтение XML-файла с помощью DOM

Допустим, нам нужно прочитать такой XML-файл:

<contacts>
    <person id="1">
        <name>Иван</name>
        <email>ivan@example.com</email>
    </person>
    <person id="2">
        <name>Мария</name>
        <email>maria@example.com</email>
    </person>
</contacts>

Код: Чтение и обход элементов

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.File;

public class DomExample {
    public static void main(String[] args) throws Exception {
        // 1. Создаём фабрику и парсер
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 2. Загружаем XML-файл в память
        Document doc = builder.parse(new File("contacts.xml"));

        // 3. Получаем корневой элемент
        Element root = doc.getDocumentElement();
        System.out.println("Корневой элемент: " + root.getTagName());

        // 4. Получаем список всех <person>
        NodeList persons = root.getElementsByTagName("person");

        for (int i = 0; i < persons.getLength(); i++) {
            Element person = (Element) persons.item(i);

            String id = person.getAttribute("id");
            String name = person.getElementsByTagName("name").item(0).getTextContent();
            String email = person.getElementsByTagName("email").item(0).getTextContent();

            System.out.println("id: " + id + ", name: " + name + ", email: " + email);
        }
    }
}

Что тут происходит:

  • Сначала создаём парсер и загружаем XML-файл.
  • Получаем корневой элемент (contacts).
  • Находим все элементы <person>, перебираем их.
  • Для каждого <person> читаем атрибут id, элементы <name> и <email>.

Схема DOM-дерева для примера

contacts
├── person (id="1")
│   ├── name ("Иван")
│   └── email ("ivan@example.com")
└── person (id="2")
    ├── name ("Мария")
    └── email ("maria@example.com")

Изменение XML с помощью DOM

DOM позволяет не только читать, но и менять XML. Например, добавим нового человека:

// Создаём новый элемент <person>
Element newPerson = doc.createElement("person");
newPerson.setAttribute("id", "3");

// <name>
Element name = doc.createElement("name");
name.setTextContent("Сергей");
newPerson.appendChild(name);

// <email>
Element email = doc.createElement("email");
email.setTextContent("sergey@example.com");
newPerson.appendChild(email);

// Добавляем к корню
root.appendChild(newPerson);

// Сохраняем изменения в файл
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(new File("contacts-updated.xml")));

Основные плюсы и минусы DOM

  • Плюсы: удобно для небольших файлов, легко делать любые изменения, можно «гулять» по дереву в любом направлении.
  • Минусы: весь XML хранится в памяти. Для больших файлов (сотни МБ и больше) — не вариант, быстро закончится память.

2. SAX (Simple API for XML)

SAX — это событийный парсер. Он не строит дерево, а просто читает XML «слева направо» и вызывает обработчики событий: «начался элемент», «текстовое содержимое», «закончился элемент» и т.д. Вы пишете свой обработчик и реагируете на нужные вам события.

Аналогия:
Если DOM — это как разложить все страницы органайзера на столе, то SAX — это будто читать органайзер постранично и делать заметки только тогда, когда встретите нужную страницу.

Основные классы SAX в Java

  • SAXParserFactory, SAXParser — фабрика и парсер.
  • DefaultHandler — базовый класс для обработки событий.
  • Методы-обработчики: startElement, characters, endElement, startDocument, endDocument.

Пример: Чтение XML-файла с помощью SAX

Допустим, тот же файл contacts.xml. Мы хотим просто вывести имена и e-mail всех людей.

Код: SAX-парсер

import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;

public class SaxExample {
    public static void main(String[] args) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        parser.parse(new File("contacts.xml"), new ContactHandler());
    }
}

class ContactHandler extends DefaultHandler {
    private String currentElement = "";
    private String name = "";
    private String email = "";

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        currentElement = qName;
        if ("person".equals(qName)) {
            String id = attributes.getValue("id");
            System.out.println("Новый человек, id: " + id);
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        String text = new String(ch, start, length).trim();
        if (text.isEmpty()) return;
        if ("name".equals(currentElement)) {
            name = text;
        } else if ("email".equals(currentElement)) {
            email = text;
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        if ("person".equals(qName)) {
            System.out.println("Имя: " + name + ", email: " + email);
            name = "";
            email = "";
        }
        currentElement = "";
    }
}

Теперь разберёмся, что здесь происходит простыми словами. Когда SAX-парсер встречает открывающий тег, например <person>, он вызывает метод startElement. Там мы сразу читаем атрибут id и выводим его на экран. Когда внутри встречается текст — например, имя или e-mail, — управление попадает в метод characters, где мы сохраняем этот текст во временные переменные. А когда парсер доходит до закрывающего тега </person>, вызывается endElement. В этот момент мы уже знаем имя и e-mail человека и можем их распечатать. После этого переменные очищаются, чтобы быть готовыми к следующему контакту.

Идея в том, что SAX не хранит весь XML целиком в памяти, а работает как потоковый «чтец»: проходит файл сверху вниз и реагирует на события — начало тега, текст, конец тега. Это быстро и экономно по памяти, особенно для больших файлов.

Краткое сравнение DOM и SAX

DOM SAX
Стиль Дерево (вся структура в памяти) События (обработка «на лету»)
Память Загружает весь XML Минимальное использование памяти
Изменения Можно читать/изменять/добавлять Только чтение (обычно)
Поиск данных Легко искать в любом месте Нужно отслеживать «где ты сейчас»
Размер файла Для маленьких и средних файлов Для очень больших файлов

3. Когда использовать DOM, а когда SAX

DOM отлично подходит, если:

  • Файл небольшой или средний по размеру.
  • Нужно многократно обращаться к разным частям XML.
  • Требуется изменять структуру документа.

SAX — ваш выбор, если:

  • Файл огромный, и загрузить его в память нельзя.
  • Нужно быстро «вытащить» только часть информации (например, найти все элементы <item> с определённым атрибутом).
  • Требуется высокая производительность и минимальное использование памяти.
  • Вы не планируете изменять XML, только читать.

Совет:
В реальных проектах часто используют оба подхода: DOM — для «человеческих» настроек и небольших конфигов, SAX — для обработки логов, выгрузок, больших импортов.

4. Практическое задание: маленький парсер для приложения

В вашем учебном приложении (например, «контактная книга») допустим, появилась задача: быстро подсчитать количество пользователей с e-mail на gmail.com.

DOM-решение:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("contacts.xml"));

NodeList emails = doc.getElementsByTagName("email");
int count = 0;
for (int i = 0; i < emails.getLength(); i++) {
    String email = emails.item(i).getTextContent();
    if (email.endsWith("@gmail.com")) {
        count++;
    }
}
System.out.println("Пользователей с gmail.com: " + count);

SAX-решение:

class GmailCounterHandler extends DefaultHandler {
    private String currentElement = "";
    int count = 0;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        currentElement = qName;
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        if ("email".equals(currentElement)) {
            String email = new String(ch, start, length).trim();
            if (email.endsWith("@gmail.com")) {
                count++;
            }
        }
    }
    @Override
    public void endDocument() {
        System.out.println("Пользователей с gmail.com: " + count);
    }
}

5. Особенности и нюансы работы с DOM и SAX

  • DOM может «подъесть» всю память, если XML-файл очень большой. Если вы вдруг увидите ошибку OutOfMemoryError, скорее всего, пора переходить на SAX.
  • SAX требует хорошей внимательности: нужно отслеживать, в каком элементе вы находитесь, и аккуратно собирать нужные данные. Иногда приходится использовать стек или дополнительные переменные, чтобы не запутаться в глубоко вложенных структурах.
  • В SAX метод characters может вызываться несколько раз для одного и того же текстового блока (особенно если текст длинный или содержит спецсимволы). Лучше накапливать текст в StringBuilder.
  • DOM отлично подходит для поиска, навигации и изменения структуры, но не для потоковой обработки.
  • Если вы не уверены, какой подход выбрать — начните с DOM для простоты, а если станет «тяжело» по памяти — перепишите на SAX.

6. Типичные ошибки при работе с DOM и SAX

Ошибка №1: Неправильная обработка пробелов и переносов строк в SAX.
Метод characters может возвращать кусочки текста, в том числе пробелы и переносы строк между элементами. Если не фильтровать .trim().isEmpty(), можно получить много «пустых» вызовов или некорректно собрать текст.

Ошибка №2: Попытка изменить XML с помощью SAX.
SAX — это только для чтения! Если нужно менять структуру — используйте DOM.

Ошибка №3: Нарушение порядка событий в SAX.
Если переменные не сбрасываются в endElement, можно получить «протечку» данных между элементами.

Ошибка №4: Использование DOM для огромных файлов.
Результат — OutOfMemoryError или очень медленная работа.

Ошибка №5: Неправильное приведение типов в DOM.
В DOM всё — это Node, но для работы с атрибутами и дочерними элементами нужно приводить к Element. Ошибка в приведении может привести к ClassCastException.

1
Задача
JAVA 25 SELF, 47 уровень, 1 лекция
Недоступна
Опознание Главного Раздела Библиотеки 🏛️
Опознание Главного Раздела Библиотеки 🏛️
1
Задача
JAVA 25 SELF, 47 уровень, 1 лекция
Недоступна
Список Персонажей или Пользователей в Системе 👤
Список Персонажей или Пользователей в Системе 👤
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Gans Electro Уровень 59
5 октября 2026
И 2 года назад когда проходил старый Java курс, и 1 год назад проходя Web курс эти темы про сериализацию JSON и особенно XML были душные. Такие темы очень сложные когда учишь их впервые, они не запоминаются. Нужно просто пару раз запарсить реальный XML. Вот тогда да, понимаешь насколько все это удобно. DOM ладно, многословный и понятный. Но в SAX легко ошибиться, особенно если не писал ее никогда. И вообще я уверен что есть гораздо более лучшие вещи чем SAX, скажем SuperDOM не знаю. SAX это буквально как писать свои парсер на коленке