1. Введение
В этом руководстве мы опишем, как преобразовать XML в HTML, используя распространенные библиотеки Java и механизмы шаблонов — JAXP, StAX, Freemarker и Mustache .
2. XML для демаршалирования
Давайте начнем с простого XML-документа, который мы преобразуем в подходящее представление Java, прежде чем преобразовывать в HTML. Мы будем иметь в виду несколько ключевых целей:
- Сохраняйте один и тот же XML для всех наших образцов
- В конце создайте синтаксически и семантически корректный документ HTML5.
- Преобразование всех элементов 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 .