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

Преобразование XML в HTML в Java

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

1. Введение

В этом руководстве мы опишем, как преобразовать XML в HTML, используя распространенные библиотеки Java и механизмы шаблонов — JAXP, StAX, Freemarker и Mustache .

2. XML для демаршалирования

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

  1. Сохраняйте один и тот же XML для всех наших образцов
  2. В конце создайте синтаксически и семантически корректный документ HTML5.
  3. Преобразование всех элементов XML в текст

Давайте используем простое уведомление Jenkins в качестве примера XML:

<?xml version="1.0" encoding="UTF-8"?>
<notification>
<from>builds@foreach.com</from>
<heading>Build #7 passed</heading>
<content>Success: The Jenkins CI build passed</content>
</notification>

И это довольно просто. Он включает в себя корневой элемент и несколько вложенных элементов.

Мы постараемся удалить все уникальные теги XML и распечатать пары ключ-значение при создании нашего HTML-файла.

3. ДЖАКСП

Архитектура Java для обработки XML ( JAXP ) — это библиотека, предназначенная для расширения функциональности популярного синтаксического анализатора SAX за счет дополнительной поддержки DOM. JAXP предоставляет возможность маршалировать и демаршалировать объекты, определенные XML, в POJO и из них с помощью SAX Parser . Мы также будем использовать встроенные помощники DOM.

Давайте добавим в наш проект зависимость Maven для JAXP :

<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxp-api</artifactId>
<version>1.4.2</version>
</dependency>

3.1. Разупорядочение с помощью DOM Builder

Давайте начнем с того, что сначала разместим наш XML-файл в объект Java Element :

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

Document input = factory
.newDocumentBuilder()
.parse(resourcePath);
Element xml = input.getDocumentElement();

3.2. Извлечение содержимого файла XML на карте

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

Map<String, String> map = new HashMap<>();
map.put("heading",
xml.getElementsByTagName("heading")
.item(0)
.getTextContent());
map.put("from", String.format("from: %s",
xml.getElementsByTagName("from")
.item(0)
.getTextContent()));
map.put("content",
xml.getElementsByTagName("content")
.item(0)
.getTextContent());

3.3. Маршалинг с помощью DOM Builder

Преобразование нашего XML в файл HTML требует немного больше усилий.

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

Document doc = factory
.newDocumentBuilder()
.newDocument();

Далее мы заполним документ элементами нашей карты :

Element html = doc.createElement("html");

Element head = doc.createElement("head");
html.setAttribute("lang", "en");

Element title = doc.createElement("title");
title.setTextContent(map.get("heading"));

head.appendChild(title);
html.appendChild(head);

Element body = doc.createElement("body");

Element from = doc.createElement("p");
from.setTextContent(map.get("from"));

Element success = doc.createElement("p");
success.setTextContent(map.get("content"));

body.appendChild(from);
body.appendChild(success);

html.appendChild(body);
doc.appendChild(html);

Наконец, давайте упорядочим наш объект Document с помощью TransformerFactory :

TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");

try (Writer output = new StringWriter()) {
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(output));
}

Если мы вызовем output.toString() , мы получим HTML-представление.

Обратите внимание, что некоторые дополнительные функции и атрибуты, которые мы установили на нашей фабрике, были взяты из рекомендаций проекта OWASP, чтобы избежать внедрения XXE .

4. Стакс

Еще одна библиотека, которую мы можем использовать, — это Streaming API for XML ( StAX ). Как и JAXP, StAX существует уже давно — с 2004 года.

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

Давайте добавим в наш проект зависимость Maven для StAX API :

<dependency>
<groupId>javax.xml.stream</groupId>
<artifactId>stax-api</artifactId>
<version>1.0-2</version>
</dependency>

4.1. Демаршаллинг с помощью StAX

Мы будем использовать простой поток управления итерацией для сохранения значений XML в нашей карте :

XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
XMLStreamReader input = null;
try (FileInputStream file = new FileInputStream(resourcePath)) {
input = factory.createXMLStreamReader(file);

Map<String, String> map = new HashMap<>();
while (input.hasNext()) {
input.next();
if (input.isStartElement()) {
if (input.getLocalName().equals("heading")) {
map.put("heading", input.getElementText());
}
if (input.getLocalName().equals("from")) {
map.put("from", String.format("from: %s", input.getElementText()));
}
if (input.getLocalName().equals("content")) {
map.put("content", input.getElementText());
}
}
}
} finally {
if (input != null) {
input.close();
}
}

4.2. Маршаллинг с использованием StAX

Теперь давайте воспользуемся нашей картой и напишем HTML :

try (Writer output = new StringWriter()) {
XMLStreamWriter writer = XMLOutputFactory
.newInstance()
.createXMLStreamWriter(output);

writer.writeDTD("<!DOCTYPE html>");
writer.writeStartElement("html");
writer.writeAttribute("lang", "en");
writer.writeStartElement("head");
writer.writeDTD("<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
writer.writeStartElement("title");
writer.writeCharacters(map.get("heading"));
writer.writeEndElement();
writer.writeEndElement();

writer.writeStartElement("body");

writer.writeStartElement("p");
writer.writeCharacters(map.get("from"));
writer.writeEndElement();

writer.writeStartElement("p");
writer.writeCharacters(map.get("content"));
writer.writeEndElement();

writer.writeEndElement();
writer.writeEndDocument();
writer.flush();
}

Как и в примере с JAXP, мы можем вызвать output.toString() , чтобы получить HTML-представление.

5. Использование шаблонизаторов

В качестве альтернативы написанию HTML-представления мы можем использовать шаблонизаторы . В экосистеме Java есть несколько вариантов. Давайте рассмотрим некоторые из них.

5.1. Использование Apache FreeMarker

Apache FreeMarker — это механизм шаблонов на основе Java для создания текстового вывода (веб-страницы HTML, сообщения электронной почты, файлы конфигурации, исходный код и т. д.) на основе шаблонов и изменяющихся данных.

Чтобы использовать его, нам нужно добавить зависимость freemarker в наш проект Maven :

<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>

Во-первых, давайте создадим шаблон, используя синтаксис FreeMarker:

<!DOCTYPE html>
<html lang="en">
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${heading}</title>
</head>
<body>
<p>${from}</p>
<p>${content}</p>
</body>
</html>

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

Configuration cfg = new Configuration(Configuration.VERSION_2_3_29);
cfg.setDirectoryForTemplateLoading(new File(templateDirectory));
cfg.setDefaultEncoding(StandardCharsets.UTF_8.toString());
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setFallbackOnNullLoopVariable(false);
Template temp = cfg.getTemplate(templateFile);
try (Writer output = new StringWriter()) {
temp.process(staxTransformer.getMap(), output);
}

5.2. Использование усов

Mustache — это шаблонизатор без логики. Mustache можно использовать для HTML, конфигурационных файлов, исходного кода — практически для чего угодно. Он работает путем расширения тегов в шаблоне с использованием значений, предоставленных в хеше или объекте.

Чтобы использовать его, нам нужно добавить зависимость усов в наш проект Maven :

<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.6</version>
</dependency>

Приступим к созданию шаблона с использованием синтаксиса Mustache:

<!DOCTYPE html>
<html lang="en">
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{heading}}</title>
</head>
<body>
<p>{{from}}</p>
<p>{{content}}</p>
</body>
</html>

Теперь давайте заполним шаблон нашей картой :

MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile(templateFile);
try (Writer output = new StringWriter()) {
mustache.execute(output, staxTransformer.getMap());
output.flush();
}

6. Результирующий HTML

В конце концов, со всеми нашими примерами кода мы получим один и тот же вывод HTML:

<!DOCTYPE html>
<html lang="en">
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Build #7 passed</title>
</head>
<body>
<p>from: builds@foreach.com</p>
<p>Success: The Jenkins CI build passed</p>
</body>
</html>

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

В этом руководстве мы изучили основы использования JAXP, StAX, Freemarker и Mustache для преобразования XML в HTML.

Для получения дополнительной информации о XML в Java ознакомьтесь с другими замечательными ресурсами прямо здесь, на ForEach:

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