«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(), который вернет список узлов:

@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>[email protected]</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("[email protected]"));
    first.appendChild(email);

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

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

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

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

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


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.