1. 简介
本文将介绍如何使用 Java 中常见的库和模板引擎,将 XML 数据转换为 HTML 页面。我们将涵盖以下几种主流方案:
✅ JAXP(DOM 解析)
✅ StAX(流式解析)
✅ Freemarker 模板引擎
✅ Mustache 模板引擎
这些方法各有适用场景:DOM 适合小文件、结构清晰的数据;StAX 更适合大文件或需要精细控制解析过程的场景;而模板引擎则更适合需要频繁生成 HTML、邮件或配置文件的业务系统。
2. 示例 XML 数据
我们统一使用以下 Jenkins 构建通知作为输入 XML,确保所有示例保持一致:
<?xml version="1.0" encoding="UTF-8"?>
<notification>
<from>jenkins@company.com</from>
<heading>Build #7 passed</heading>
<content>Success: The Jenkins CI build passed</content>
</notification>
目标很明确:
- 所有示例共用同一份 XML
- 输出符合 HTML5 标准的完整文档结构
- 将 XML 元素内容提取并渲染成纯文本展示
最终期望生成的 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: jenkins@company.com</p>
<p>Success: The Jenkins CI build passed</p>
</body>
</html>
3. 使用 JAXP(DOM 方式)
JAXP(Java API for XML Processing)是 Java 内置的标准 XML 处理框架,支持 DOM 和 SAX 解析。这里我们使用 DOM 方式进行演示。
3.1 Maven 依赖
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxp-api</artifactId>
<version>1.4.2</version>
</dependency>
⚠️ 注意:虽然 jaxp-api
是标准包,但在现代项目中通常已默认包含,无需显式引入。
3.2 使用 DOM 解析 XML
先将 XML 解析为 Document
对象,再提取所需字段:
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(new File(resourcePath));
Element root = input.getDocumentElement();
📌 安全提示:禁用 DTD 是防止 XXE 攻击的关键步骤,生产环境务必加上。
3.3 提取数据到 Map
Map<String, String> map = new HashMap<>();
map.put("heading",
root.getElementsByTagName("heading").item(0).getTextContent());
map.put("from", "from: " +
root.getElementsByTagName("from").item(0).getTextContent());
map.put("content",
root.getElementsByTagName("content").item(0).getTextContent());
简单粗暴,直接按标签名取值,适合结构固定的 XML。
3.4 使用 DOM 构建 HTML
接下来用 DOM API 手动生成 HTML 文档:
Document doc = factory.newDocumentBuilder().newDocument();
Element html = doc.createElement("html");
html.setAttribute("lang", "en");
Element head = doc.createElement("head");
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"));
body.appendChild(from);
Element content = doc.createElement("p");
content.setTextContent(map.get("content"));
body.appendChild(content);
html.appendChild(body);
doc.appendChild(html);
3.5 输出 HTML 字符串
最后通过 Transformer
将 DOM 树转为字符串:
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));
String htmlString = output.toString(); // 获取结果
}
✅ 优点:API 稳定,适合结构化强的小型 XML
❌ 缺点:内存占用高,不适合大文件
4. 使用 StAX(流式解析)
StAX(Streaming API for XML)是一种“拉模式”解析器,允许我们逐个读取事件,适合处理大型 XML 文件。
4.1 Maven 依赖
<dependency>
<groupId>javax.xml.stream</groupId>
<artifactId>stax-api</artifactId>
<version>1.0-2</version>
</dependency>
4.2 使用 StAX 解析 XML
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
XMLStreamReader reader = null;
try (FileInputStream file = new FileInputStream(resourcePath)) {
reader = factory.createXMLStreamReader(file);
Map<String, String> map = new HashMap<>();
while (reader.hasNext()) {
reader.next();
if (reader.isStartElement()) {
switch (reader.getLocalName()) {
case "heading":
map.put("heading", reader.getElementText());
break;
case "from":
map.put("from", "from: " + reader.getElementText());
break;
case "content":
map.put("content", reader.getElementText());
break;
}
}
}
} finally {
if (reader != null) reader.close();
}
📌 踩坑提醒:一定要关闭 XMLStreamReader
,否则可能泄露文件句柄。
4.3 使用 StAX 生成 HTML
虽然 StAX 主要用于读取,但 XMLOutputFactory
也可以用来写入:
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(); // title
writer.writeEndElement(); // head
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(); // body
writer.writeEndElement(); // html
writer.writeEndDocument();
writer.flush();
String html = output.toString();
}
✅ 优点:内存友好,可处理大文件
❌ 缺点:代码略繁琐,容易漏写 writeEndElement
5. 使用模板引擎
如果你需要频繁生成 HTML 或邮件内容,强烈建议使用模板引擎。它们更易维护、扩展性更好。
5.1 使用 Freemarker
Freemarker 是 Java 圈最流行的模板引擎之一,功能强大,支持复杂逻辑。
Maven 依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
模板文件(template.ftl
)
<!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>
Java 渲染代码
Configuration cfg = new Configuration(Configuration.VERSION_2_3_29);
cfg.setDirectoryForTemplateLoading(new File(templateDirectory));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
Template template = cfg.getTemplate("template.ftl");
try (StringWriter output = new StringWriter()) {
template.process(map, output);
String html = output.toString();
}
📌 提示:process()
第二个参数可以是 Map、POJO 或任何可迭代对象。
5.2 使用 Mustache
Mustache 是“逻辑-less”模板引擎,强调简洁,适合不想在模板里写逻辑的场景。
Maven 依赖
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.6</version>
</dependency>
模板文件(template.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>
Java 渲染代码
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile("template.mustache");
try (StringWriter output = new StringWriter()) {
mustache.execute(output, map).flush();
String html = output.toString();
}
✅ 优点:模板干净,不易写出复杂逻辑
❌ 缺点:灵活性较低,条件判断需依赖外部数据预处理
6. 最终输出结果
无论使用哪种方式,最终生成的 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: jenkins@company.com</p>
<p>Success: The Jenkins CI build passed</p>
</body>
</html>
7. 总结
方法 | 适用场景 | 推荐指数 |
---|---|---|
JAXP (DOM) | 小型、结构固定 XML | ⭐⭐⭐ |
StAX | 大文件、需精细控制解析 | ⭐⭐⭐⭐ |
Freemarker | 需要频繁生成 HTML/邮件 | ⭐⭐⭐⭐⭐ |
Mustache | 模板简单、避免逻辑嵌入 | ⭐⭐⭐⭐ |
📌 核心建议:
- 如果只是偶尔转换一次,用 DOM 最快上手
- 处理大 XML 文件?选 StAX,避免 OOM
- 涉及邮件、通知模板?直接上 Freemarker,维护成本低得多
所有完整示例代码已上传至 GitHub:https://github.com/baeldung/tutorials/tree/master/xml
更多 XML 相关文章推荐: