Как читать и записывать XML-файлы с помощью Java

1
компьютеры и технологии 8.webp.webp

Последнее обновление 12.09.2023 — Василий Иванов

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

Несмотря на то, что популярность XML в последние годы пошла на убыль, время от времени вы можете с ним столкнуться, поэтому важно научиться с ним работать. Узнайте, как использовать DOM API для чтения и записи XML-файлов с помощью Java.

Требования для обработки XML в Java

Java Standard Edition (SE) включает Java API для обработки XML (JAXP), который представляет собой общий термин, охватывающий большинство аспектов обработки XML. К ним относятся:

  • DOM: объектная модель документа включает классы для работы с объектами XML, такими как элементы, узлы и атрибуты. DOM API загружает весь XML-документ в память для обработки, поэтому он не очень подходит для больших XML-файлов.
  • SAX: Simple API for XML — это управляемый событиями API для чтения XML. Он генерирует события в ответ на содержимое XML, которое он находит при анализе файла. Затраты памяти у этого метода невелики, но работать с API сложнее, чем с DOM.
  • StAX: API потоковой передачи для XML появился недавно. Он обеспечивает высокопроизводительную фильтрацию потоков, обработку и модификацию XML. Хотя он позволяет избежать загрузки всего XML-документа в память, он обеспечивает архитектуру по запросу, а не архитектуру, управляемую событиями, поэтому его проще кодировать, чем SAX API.
По теме:  Как пометить сообщения как прочитанные в Сообщениях Google

Для обработки XML в Java вам необходимо импортировать эти пакеты:

 import javax.xml.parsers.*;
import javax.xml.transform.*;
import org.w3c.dom.*;

Подготовка образца XML-файла

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

 <?xml version="1.0"?>
<catalog>
  <book id="bk101">
    <author>Gambardella, Matthew</author>
    <title>XML Developer's Guide</title>
    <genre>Computer</genre>
    <price>44.95</price>
    <publish_date>2000-10-01</publish_date>
    <description>An in-depth look at creating applications
      with XML.</description>
  </book>
  <book id="bk102">
    <author>Ralls, Kim</author>
...snipped...

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

Давайте рассмотрим основные шаги, необходимые для чтения XML-файла с помощью DOM API. Начните с создания экземпляра DocumentBuilder, который вы будете использовать для анализа XML-документа:

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

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

 // XML file to read
File file = "<path_to_file>";
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();

Вот и все; теперь у вас есть доступ ко всему XML-документу, начиная с его корневого элемента — каталога.

Извлечение информации с помощью DOM API

Теперь, когда у вас есть корневой элемент XML, вы можете использовать DOM API для извлечения интересных фрагментов информации. Например, возьмите все дочерние элементы book корневого элемента и переберите их. Обратите внимание, что 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;
  // work with the book Element here
}

Как найти конкретный дочерний элемент по родительскому элементу? Создайте статический метод, который возвращает первый соответствующий элемент, если он найден, или значение NULL. Процедура включает в себя получение списка дочерних узлов и циклическое перебор их, выбирая узлы элементов с указанным именем.

 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 API рассматривает текстовое содержимое внутри элемента как отдельный узел типа 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();
}

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

 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);
}

Вот пошаговое объяснение кода:

  1. Код перебирает дочерние узлы каталога, корневой элемент.
  2. Для каждого дочернего узла, представляющего книгу, проверяется, является ли тип узла ELEMENT_NODE. Если нет, он переходит к следующей итерации.
  3. Если дочерний узел является ELEMENT_NODE, (Element)child преобразует его в объект Element.
  4. Затем код извлекает из элемента книги различные атрибуты и символьные данные, включая «идентификатор», «автор», «название», «жанр», «цену», «дату публикации» и «описание». Он печатает эти данные с помощью метода System.out.printf.

Вот как выглядит результат:

Написание XML-вывода с использованием Transform API

Java предоставляет API преобразования XML для преобразования данных XML. Мы используем этот API с преобразованием идентичности для генерации вывода. В качестве примера добавим новый элемент книги в образец каталога, представленный выше.

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

 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, that a single man in possession of a good fortune must be in want of a wife." So begins Pride and Prejudice, Jane Austen's witty comedy of manners-one of the most popular novels of all time-that features splendidly civilized sparring between the proud Mr. Darcy and the prejudiced Elizabeth Bennet as they play out their spirited courtship in a series of eighteenth-century drawing-room intrigues.

Первым шагом является анализ существующего XML-файла с использованием метода, представленного выше:

 File file = ...; // XML file to read
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();

Теперь вы загружаете данные из файла свойств, используя класс Properties, предусмотренный в Java. Код довольно прост:

 String propsFile = "<path_to_file>";
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");

Теперь создайте пустой элемент книги.

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

Добавление дочерних элементов в книгу тривиально. Для удобства вы можете собрать имена необходимых элементов в список и добавлять значения в цикле.

 List<String> 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);

В элемент каталога теперь добавлен новый элемент книги. Теперь остается только записать обновленный XML.

Чтобы написать XML, вам понадобится экземпляр Transformer, который вы можете создать следующим образом:

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

Вы можете использовать setOutputProperty() для запроса отступа вывода.

Последний шаг — применить преобразование. Результат появляется в выходном потоке System.out.

 tform.transform(new DOMSource(document), new StreamResult(System.out));

Чтобы записать вывод непосредственно в файл, используйте следующее:

 tform.transform(new DOMSource(document), new StreamResult(new File("output.xml")));

Это все шаги, необходимые для чтения и записи XML-файлов на Java.

Теперь вы знаете, как читать и записывать XML-файлы с помощью Java.

Анализ XML и манипулирование им с помощью Java — ценный навык, который вы часто будете использовать в реальных программах. API-интерфейсы DOM и Transform особенно полезны.

Понимание DOM, в частности, жизненно важно, если вы планируете писать клиентский код для веб-приложений или сайтов. Интерфейс DOM универсален, поэтому вы можете работать с ним, используя аналогичный код на таких разных языках, как Java и JavaScript.