Перейти к основному содержимому

Работа с файлами XML в Java с использованием синтаксического анализа DOM

· 4 мин. чтения

1. Обзор

В этом руководстве мы обсудим, как анализировать DOM с помощью Apache Xerces — зрелой и проверенной библиотеки для анализа/манипулирования XML.

Существует несколько вариантов анализа XML-документа; в этой статье мы сосредоточимся на разборе DOM. Анализатор DOM загружает документ и создает в памяти целое иерархическое дерево.

Для обзора поддержки библиотек XML в Java ознакомьтесь с нашей предыдущей статьей .

2. Наш документ

Начнем с XML-документа, который мы будем использовать в нашем примере:

<?xml version="1.0"?>
<tutorials>
<tutorial tutId="01" type="java">
<title>Guava</title>
<description>Introduction to Guava</description>
<date>04/04/2016</date>
<author>GuavaAuthor</author>
</tutorial>
...
</tutorials>

Обратите внимание, что в нашем документе есть корневой узел под названием «tutorials» с 4 дочерними узлами «tutorial». Каждый из них имеет 2 атрибута: «tutId» и «type». Также у каждого «туториала» есть 4 дочерних узла: «название», «описание», «дата» и «автор».

Теперь мы можем продолжить разбор этого документа.

3. Загрузка XML-файла

Во- первых, мы должны отметить, что библиотека Apache Xerces упакована с JDK , поэтому нам не нужна дополнительная настройка.

Давайте сразу перейдем к загрузке нашего XML-файла:

DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new File("src/test/resources/example_jdom.xml"));
doc.getDocumentElement().normalize();

В приведенном выше примере мы сначала получаем экземпляр класса DocumentBuilder , а затем используем метод parse() для XML-документа, чтобы получить объект Document , представляющий его.

Нам также нужно использовать метод normalize() , чтобы гарантировать, что на иерархию документа не повлияют какие-либо лишние пробелы или новые строки внутри узлов.

4. Разбор DOM

Теперь давайте изучим наш XML-файл.

Начнем с извлечения всех элементов с тегом «tutorial». Мы можем сделать это с помощью метода getElementsByTagName() , который вернет NodeList:

@Test
public void whenGetElementByTag_thenSuccess() {
NodeList nodeList = doc.getElementsByTagName("tutorial");
Node first = nodeList.item(0);

assertEquals(4, nodeList.getLength());
assertEquals(Node.ELEMENT_NODE, first.getNodeType());
assertEquals("tutorial", first.getNodeName());
}

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

Далее давайте посмотрим, как мы можем получить атрибуты первого элемента, используя getAttributes() :

@Test
public void whenGetFirstElementAttributes_thenSuccess() {
Node first = doc.getElementsByTagName("tutorial").item(0);
NamedNodeMap attrList = first.getAttributes();

assertEquals(2, attrList.getLength());

assertEquals("tutId", attrList.item(0).getNodeName());
assertEquals("01", attrList.item(0).getNodeValue());

assertEquals("type", attrList.item(1).getNodeName());
assertEquals("java", attrList.item(1).getNodeValue());
}

Здесь мы получаем объект NamedNodeMap , а затем используем метод item(index) для извлечения каждого узла.

Для каждого узла мы можем использовать getNodeName() и getNodeValue() , чтобы найти их атрибуты.

5. Пересечение узлов

Далее давайте посмотрим, как проходить узлы DOM.

В следующем тесте мы пройдемся по дочерним узлам первого элемента и распечатаем их содержимое:

@Test
public void whenTraverseChildNodes_thenSuccess() {
Node first = doc.getElementsByTagName("tutorial").item(0);
NodeList nodeList = first.getChildNodes();
int n = nodeList.getLength();
Node current;
for (int i=0; i<n; i++) {
current = nodeList.item(i);
if(current.getNodeType() == Node.ELEMENT_NODE) {
System.out.println(
current.getNodeName() + ": " + current.getTextContent());
}
}
}

Сначала мы получаем NodeList с помощью метода getChildNodes() , затем перебираем его и печатаем имя узла и текстовое содержимое.

Вывод покажет содержимое первого элемента «tutorial» в нашем документе:

title: Guava
description: Introduction to Guava
date: 04/04/2016
author: GuavaAuthor

6. Изменение DOM

Мы также можем вносить изменения в DOM.

В качестве примера изменим значение атрибута type с «java» на «other»:

@Test
public void whenModifyDocument_thenModified() {
NodeList nodeList = doc.getElementsByTagName("tutorial");
Element first = (Element) nodeList.item(0);

assertEquals("java", first.getAttribute("type"));

first.setAttribute("type", "other");
assertEquals("other", first.getAttribute("type"));
}

Здесь изменение значения атрибута — это просто вызов метода setAttribute () элемента .

7. Создание нового документа

Помимо изменения DOM, мы также можем создавать новые XML-документы с нуля.

Давайте сначала посмотрим на файл, который мы хотим создать:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<users>
<user id="1">
<email>john@example.com</email>
</user>
</users>

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

Чтобы достичь этого, мы сначала должны вызвать метод newDocument() Builder , который возвращает объект Document .

Затем мы вызовем метод createElement() нового объекта:

@Test
public void whenCreateNewDocument_thenCreated() throws Exception {
Document newDoc = builder.newDocument();
Element root = newDoc.createElement("users");
newDoc.appendChild(root);

Element first = newDoc.createElement("user");
root.appendChild(first);
first.setAttribute("id", "1");

Element email = newDoc.createElement("email");
email.appendChild(newDoc.createTextNode("john@example.com"));
first.appendChild(email);

assertEquals(1, newDoc.getChildNodes().getLength());
assertEquals("users", newDoc.getChildNodes().item(0).getNodeName());
}

Чтобы добавить каждый элемент в DOM, мы также вызываем метод appendChild() .

8. Сохранение документа

После изменения нашего документа или создания его с нуля нам нужно сохранить его в файле.

Мы начнем с создания объекта DOMSource , а затем воспользуемся простым преобразователем , чтобы сохранить документ в файл:

private void saveDomToFile(Document document,String fileName) 
throws Exception {

DOMSource dom = new DOMSource(document);
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();

StreamResult result = new StreamResult(new File(fileName));
transformer.transform(dom, result);
}

Точно так же мы можем распечатать наш документ в консоли:

private void printDom(Document document) throws Exception{
DOMSource dom = new DOMSource(document);
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();

transformer.transform(dom, new StreamResult(System.out));
}

9. Заключение

В этой быстрой статье мы узнали, как использовать парсер Xerces DOM для создания, изменения и сохранения XML-документа.

Как всегда, полный исходный код примеров доступен на GitHub .