Java中HTML字符转义标准化/归一化实现方案咨询(需输出可安全解析的HTML)
Java中HTML字符转义标准化/归一化实现方案咨询(需输出可安全解析的HTML)
嗨,我太懂你这个需求的痛点了——既要把各种花里胡哨的HTML字符转义(比如À的十进制、十六进制、实体名称转义)统一成标准形式,又得保证最终输出的HTML还是能安全解析,不能把原本的标签给搞坏,甚至连>这种嵌套转义的情况都得处理好,对吧?
我之前也碰到过类似的问题:直接用StringEscapeUtils.unescapeHtml4再escapeHtml4的操作完全不可行,因为会把HTML标签里的<、>也给转义成<、>,直接破坏整个HTML结构;而只处理转义又没法区分哪些是文本里的转义、哪些是标签的一部分。
给你分享一套亲测有效的实现思路,核心是先解析HTML结构,精准区分标签和文本,再单独标准化文本内容的转义:
核心思路拆解
- 先把HTML解析成DOM结构:这样能精准区分「HTML标签(包括属性)」和「文本内容」——只有文本内容里的转义需要标准化,标签本身的半角符号绝对不能碰。
- 对文本节点做「先全反转义,再标准转义」的操作:
- 把文本里所有的转义(不管是十进制、十六进制还是实体名称)都转成原始字符;
- 再把这些原始字符转成标准的HTML转义(比如À会被
escapeHtml4转成À,优先用实体名称)。
- 把处理后的DOM转回HTML字符串:这样标签部分完全保留原始结构,只有文本里的转义被标准化,最终的HTML是完全可安全解析的。
具体代码实现(结合Apache Commons Text + Jsoup)
因为你已经在用Apache Commons Text了,再配合Jsoup(专门处理HTML解析的工具)就能完美解决问题,代码示例如下:
import org.apache.commons.text.StringEscapeUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import org.jsoup.nodes.TextNode; import org.jsoup.select.NodeTraversor; import org.jsoup.select.NodeVisitor; public class HtmlEscapeNormalizer { public static String normalizeHtmlEscapes(String originalHtml) { // 用XML解析器避免Jsoup自动补全HTML标签,严格保留原始结构 Document doc = Jsoup.parse(originalHtml, "", org.jsoup.parser.Parser.xmlParser()); // 遍历所有节点,精准处理文本和属性 new NodeTraversor(new NodeVisitor() { @Override public void head(Node node, int depth) { if (node instanceof TextNode) { TextNode textNode = (TextNode) node; // 1. 全反转义:把所有转义形式转成原始字符 String unescapedText = StringEscapeUtils.unescapeHtml4(textNode.text()); // 2. 标准转义:把原始字符转成统一的HTML转义形式 String normalizedText = StringEscapeUtils.escapeHtml4(unescapedText); textNode.text(normalizedText); } else if (node instanceof Element) { Element element = (Element) node; // 同时处理元素属性里的转义 for (String attrKey : element.attributes().keySet()) { String attrValue = element.attr(attrKey); String unescapedAttr = StringEscapeUtils.unescapeHtml4(attrValue); String normalizedAttr = StringEscapeUtils.escapeHtml4(unescapedAttr); element.attr(attrKey, normalizedAttr); } } } @Override public void tail(Node node, int depth) { // 不需要额外收尾操作 } }).traverse(doc); // 把处理后的DOM转回HTML字符串 return doc.html(); } }
代码说明
- 用Jsoup的XML解析器是为了避免它自动补全
<html>、<body>这类标签,严格保留你输入的原始HTML片段结构; - 只处理文本节点和属性值里的转义,标签本身的半角符号(比如
<div>里的<)完全不动,绝对不会破坏HTML结构; - 像
&#x00C0;、&#192;、À这类转义,最终都会被统一成À(escapeHtml4优先用实体名称); - 对于
&gt;这种嵌套转义,处理后会变成标准的>,既保留了原本要显示>的意图,又能被HTML安全解析。
额外提醒
如果你不想引入Jsoup依赖,只用Apache Commons Text单独处理几乎是不可能的——因为没有HTML结构解析的话,根本没法区分标签和文本,很容易把标签里的字符误转义,导致HTML完全失效。所以还是推荐用上面的方案,这是最稳妥的。
内容来源于stack exchange
相关产品推荐
相关产品推荐

