Статьи

Как отформатировать XML

24 марта 2021

Тэги: XML Java Kotlin

Как известно, формат xml нечувствителен к отступам и пробелам между тэгами. Поэтому с целью уменьшения объема данных и без того многословный xml можно записать в одну строку. Тогда вы получите заметное уменьшение объёма, но при этом такой формат будет сложно просматривать.

Чтобы сделать его удобочитаемым, мы можем написать специальный метод, который будет считывать исходный xml, форматировать его по определённым правилам и возвращать новый xml в виде строки. Форматирование будем производить без сторонних библиотек стандартными средствами JDK.

Рассмотрим следующий пример на kotlin:

fun xmlPrettyPrint(sourceXml: String, spaceCount: Int): String {
    val factory = DocumentBuilderFactory.newInstance()
    val builder = factory.newDocumentBuilder()

Наш метод xmlPrettyPrint() принимает два параметра: исходный неформатированный xml и количество пробелов, которые требуются для одного отступа (вместо символа табуляции).

В начале создаём новый DocumentBuilder с помощью класса javax.xml.parsers.DocumentBuilderFactory. Затем в блоке try пытаемся прочитать исходный xml. Обратите внимание, что строка с xml не должна быть пустой. Она должна содержать валидный xml.

try {
    val document = builder.parse(sourceXml.byteInputStream())
    document.xmlStandalone = true
   // ... настройка трансформера для форматирования xml
} catch (e: SAXParseException) {
    throw RuntimeException("Некорректный формат XML")
} catch (e: Exception) {
    throw RuntimeException("Ошибка при форматировании XML")
}

Тут мы перехватываем некоторые исключения, которые могут возникнуть при парсинге xml. Например, SAXParseException.

Далее создаём трансформер с помощью javax.xml.transform.TransformerFactory и настраиваем его нужным образом:

val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
// этот параметр нужен для перевода строки после объявления XML
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "")
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4")

Все свойства устанавливаются с помощью метода setOutputProperty():

  1. OMIT_XML_DECLARATION - пропустить стандартный заголовок xml, доступные значения yes или no. Можно поставить yes, т.к. по большому счёту смысловой нагрузки он не несёт.
  2. DOCTYPE_PUBLIC - устанавливаем пустую строку, чтобы после заголовка был добавлен перевод строки.
  3. INDENT - признак того, что нужно делать отступы для вложенных элементов.
  4. Свойство со сложночитаемым названием {http://xml.apache.org/xslt}indent-amount - позволяет указать количество пробелов на один отступ. Я предлагаю указывать 4 пробела.

Теперь нам осталось применить эти правила и получить новую строку с отформатированным xml:

val stringWriter = StringWriter()
transformer.transform(DOMSource(document), StreamResult(stringWriter))
return stringWriter.toString()

Здесь используем метод transform(), в который передаём исходный объект документа, оборачивая его в класс javax.xml.transform.dom.DOMSource и новый объект StringWriter, обёрнутый в javax.xml.transform.stream.StreamResult. Возвращаем результат работы нашего метода, полученный в stringWriter.

Теперь мы готовы протестировать логику работы нашего метода. Передадим в него такую строку:

<items><item1>some</item1><item2>text</item2></items>

Метод вернёт такой результат:

<items>
    <item1>some</item1>
    <item2>text</item2>
</items>

Как видите, элементы списка были отформатированы с учётом вложенности, сделав исходный xml более читаемым.



Комментарии

Добавить комментарий

×

devmark.ru