1. Обзор
Когда нам нужно прочитать файл XML вручную, обычно мы хотели бы прочитать содержимое в удобном для печати формате. Многие текстовые редакторы или IDE могут переформатировать XML-документы. Если мы работаем в Linux, мы можем красиво распечатать XML-файлы из командной строки .
Однако иногда у нас есть требования преобразовать необработанную строку XML в красиво напечатанный формат в нашей программе Java. Например, мы можем захотеть показать красиво напечатанный XML-документ в пользовательском интерфейсе для лучшего визуального понимания.
В этом руководстве мы рассмотрим, как красиво печатать XML в Java.
2. Введение в проблему
Для простоты возьмем в качестве входных данных неформатированный файл emails.xml :
<emails> <email> <from>Kai</from> <to>Amanda</to> <time>2018-03-05</time>
<subject>I am flying to you</subject></email> <email>
<from>Jerry</from> <to>Tom</to> <time>1992-08-08</time> <subject>Hey Tom, catch me if you can!</subject>
</email> </emails>
Как мы видим, файл emails.xml
имеет правильный формат. Однако читать его нелегко из-за грязного формата.
Наша цель — создать метод для преобразования этой уродливой необработанной XML-строки в красиво отформатированную строку.
Далее мы обсудим настройку двух общих выходных свойств: размер отступа ( целое число
) и подавление объявления XML ( логическое значение
).
Свойство indent-size довольно простое: это количество пробелов для отступа (на уровень). С другой стороны, опция подавления объявления XML решает, хотим ли мы иметь тег объявления XML в сгенерированном XML. Типичное объявление XML выглядит так:
<?xml version="1.0" encoding="UTF-8"?>
В этом руководстве мы рассмотрим решение со стандартным Java API и еще один подход с использованием внешней библиотеки.
Далее давайте посмотрим на них в действии.
3. Прекрасная печать XML с помощью класса Transformer
Java API предоставляет класс Transformer
для преобразования XML.
3.1. Использование трансформатора
по умолчанию ``
Во-первых, давайте посмотрим на красивое решение с использованием класса Transformer :
public static String prettyPrintByTransformer(String xmlString, int indent, boolean ignoreDeclaration) {
try {
InputSource src = new InputSource(new StringReader(xmlString));
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", indent);
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, ignoreDeclaration ? "yes" : "no");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
Writer out = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(out));
return out.toString();
} catch (Exception e) {
throw new RuntimeException("Error occurs when pretty-printing xml:\n" + xmlString, e);
}
}
Теперь давайте быстро пройдемся по методу и выясним, как он работает:
- Сначала мы анализируем необработанную строку XML и получаем объект
Document
. - Затем мы получаем экземпляр
TransformerFactory
и устанавливаем необходимый атрибут размера отступа. - Затем мы можем получить экземпляр преобразователя по умолчанию из настроенного объекта
transformerFactory .
- Объект
преобразования
поддерживает различные выходные свойства. Чтобы решить, хотим ли мы пропустить объявление, мы устанавливаем атрибутOutputKeys.OMIT_XML_DECLARATION
. - Поскольку мы хотели бы иметь хорошо отформатированный объект
String
, наконец, мыпреобразуем ()
проанализированный XML-документ
вStringWriter
и возвращаем преобразованныйString
.
Мы установили размер отступа для объекта TransformerFactory в методе выше.
В качестве альтернативы мы также можем определить свойство indent-amount
для экземпляра трансформатора
:
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent));
Далее давайте проверим, работает ли метод должным образом.
3.2. Тестирование метода
Наш Java-проект — это проект Maven, и мы поместили файл emails.xml
в src/main/resources/xml/email.xml
. Мы создали метод readFromInputStream
для чтения входного файла в виде строки
. Но мы не будем вдаваться в подробности этого метода, так как он не имеет большого отношения к нашей теме. Допустим, мы хотим установить indent-size=2 и пропустить объявление XML в результате:
public static void main(String[] args) throws IOException {
InputStream inputStream = XmlPrettyPrinter.class.getResourceAsStream("/xml/emails.xml");
String xmlString = readFromInputStream(inputStream);
System.out.println("Pretty printing by Transformer");
System.out.println("=============================================");
System.out.println(prettyPrintByTransformer(xmlString, 2, true));
}
Как показывает основной
метод, мы читаем входной файл как строку
, а затем вызываем наш метод prettyPrintByTransformer
, чтобы получить красиво напечатанную строку
XML .
Далее запустим основной
метод с Java 8 :
Pretty printing by Transformer
=============================================
<emails>
<email>
<from>Kai</from>
<to>Amanda</to>
<time>2018-03-05</time>
<subject>I am flying to you</subject>
</email>
<email>
<from>Jerry</from>
<to>Tom</to>
<time>1992-08-08</time>
<subject>Hey Tom, catch me if you can!</subject>
</email>
</emails>
Как видно из приведенного выше вывода, наш метод работает так, как ожидалось.
Однако, если мы еще раз проверим его с помощью Java 9 или более поздней версии, мы можем увидеть другой результат.
Далее давайте посмотрим, что получится, если мы запустим его с Java 9 :
Pretty printing by Transformer
=============================================
<emails>
<email>
<from>Kai</from>
<to>Amanda</to>
<time>2018-03-05</time>
<subject>I am flying to you</subject>
</email>
<email>
<from>Jerry</from>
<to>Tom</to>
<time>1992-08-08</time>
<subject>Hey Tom, catch me if you can!</subject>
</email>
</emails>
=============================================
Как видно из приведенного выше вывода, в выводе есть неожиданные пустые строки.
Это связано с тем, что наш необработанный ввод содержит пробелы между элементами, например:
<emails> <email> <from>Kai</from> ...
Начиная с Java 9 функция красивой печати класса Transformer
не определяет фактический формат. Следовательно, также будут выводиться узлы, содержащие только пробелы. Это обсуждалось в этом сообщении об ошибке JDK . Кроме того, в примечании к выпуску Java 9 это объясняется в разделе xml/jaxp.
Если мы хотим, чтобы наш метод красивой печати всегда генерировал один и тот же формат в разных версиях Java, нам нужно предоставить файл таблицы стилей.
Затем давайте создадим простой файл xsl
для достижения этой цели.
3.3. Предоставление XSLT-файла
Во-первых, давайте создадим файл prettyprint.xsl
, чтобы определить выходной формат:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Как мы видим, в файле prettyprint.xsl
мы использовали элемент < xsl:strip-space/>
для удаления узлов, состоящих только из пробелов, чтобы они не отображались в выводе .
Далее нам все еще нужно внести небольшое изменение в наш метод. Мы больше не будем использовать преобразователь по умолчанию. Вместо этого мы создадим объект Transformer
с нашим XSLT-документом :
Transformer transformer = transformerFactory.newTransformer(new StreamSource(new StringReader(readPrettyPrintXslt())));
Здесь метод readPrettyPrintXslt()
считывает содержимое prettyprint.xsl
.
Теперь, если мы протестируем метод в Java 8 и Java 9, оба выведут один и тот же результат:
Pretty printing by Transformer
=============================================
<emails>
<email>
<from>Kai</from>
<to>Amanda</to>
<time>2018-03-05</time>
<subject>I am flying to you</subject>
</email>
...
</emails>
Мы решили проблему со стандартным Java API. Далее, давайте распечатаем emails.xml
, используя внешнюю библиотеку.
4. Прекрасная печать XML с помощью библиотеки Dom4j
Dom4j — популярная XML-библиотека. Это позволяет нам легко печатать документы XML.
Во-первых, давайте добавим зависимость Dom4j в наш pom.xml
:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
В качестве примера мы использовали версию 2.1.3. Мы можем найти последнюю версию в репозитории Maven Central.
Далее давайте посмотрим, как красиво напечатать XML с помощью библиотеки Dom4j:
public static String prettyPrintByDom4j(String xmlString, int indent, boolean skipDeclaration) {
try {
OutputFormat format = OutputFormat.createPrettyPrint();
format.setIndentSize(indent);
format.setSuppressDeclaration(skipDeclaration);
format.setEncoding("UTF-8");
org.dom4j.Document document = DocumentHelper.parseText(xmlString);
StringWriter sw = new StringWriter();
XMLWriter writer = new XMLWriter(sw, format);
writer.write(document);
return sw.toString();
} catch (Exception e) {
throw new RuntimeException("Error occurs when pretty-printing xml:\n" + xmlString, e);
}
}
Класс OutputFormat D0m4j
предоставляет метод createPrettyPrint
для создания предопределенного объекта OutputFormat
красивой печати . Как показано в приведенном выше методе, мы можем добавить некоторые настройки в формат красивой печати по умолчанию. В этом случае мы устанавливаем размер отступа и решаем, хотим ли мы включить объявление в результат.
Затем мы анализируем необработанную строку XML и создаем объект XMLWritter
с подготовленным экземпляром OutputFormat
.
Наконец, объект XMLWriter
запишет проанализированный XML-документ в требуемом формате.
Затем давайте проверим, может ли он красиво распечатать файл emails.xml
. На этот раз, скажем, мы хотели бы включить объявление и получить отступ размером 8 в результате:
System.out.println("Pretty printing by Dom4j");
System.out.println("=============================================");
System.out.println(prettyPrintByDom4j(xmlString, 8, false));
Когда мы запустим метод, мы увидим вывод:
Pretty printing by Dom4j
=============================================
<?xml version="1.0" encoding="UTF-8"?>
<emails>
<email>
<from>Kai</from>
<to>Amanda</to>
<time>2018-03-05</time>
<subject>I am flying to you</subject>
</email>
<email>
<from>Jerry</from>
<to>Tom</to>
<time>1992-08-08</time>
<subject>Hey Tom, catch me if you can!</subject>
</email>
</emails>
Как видно из приведенного выше вывода, метод решил проблему.
5. Вывод
В этой статье мы рассмотрели два подхода к красивой печати XML-файла в Java.
Мы можем красиво печатать XML, используя стандартный Java API. Однако мы должны помнить, что объект Transformer
может давать разные результаты в зависимости от версии Java. Решение состоит в том, чтобы предоставить файл XSLT.
В качестве альтернативы, библиотека Dom4j может решить проблему напрямую.
Как всегда, полная версия кода доступна на GitHub .