
当XML字符串被连续多次转义(如
当xml字符串被连续多次转义(如 `<`)时,`stringescapeutils.unescapexml()` 仅执行单层解码;需循环调用直至无变化,或根据预期转义层数精确调用多次。
在实际开发中,尤其在处理来自不可信来源(如旧系统导出、嵌套模板渲染、HTTP参数二次编码)的XML/HTML片段时,字符串可能被反复转义多次。例如
- 第一次转义:
→ <errors> - 第二次转义:<errors> 中的 & 和 ; 被再次转义 →
而 org.apache.commons.lang3.StringEscapeUtils.unescapeXml() 的设计原则是严格单层解码——它只识别并还原标准XML实体(如 <, >, &, ", '),不会递归解析解码后新产生的实体。因此直接调用一次只会将 。
✅ 正确做法是:按已知转义层数精确调用多次,或采用安全的循环解码策略:
import org.apache.commons.lang3.StringEscapeUtils;
public class XmlUnescapeExample {
public static void main(String[] args) {
String input = "<errors>";
// 方案1:明确知道转义2层 → 调用2次
String output = StringEscapeUtils.unescapeXml(input);
output = StringEscapeUtils.unescapeXml(output);
System.out.println(output); // 输出:<errors>
// 方案2:通用鲁棒解法(自动迭代至稳定)
String result = unescapeXmlRecursively(input);
System.out.println(result); // 同样输出:<errors>
}
public static String unescapeXmlRecursively(String input) {
if (input == null) return null;
String current = input;
String next;
do {
next = StringEscapeUtils.unescapeXml(current);
if (next.equals(current)) break; // 解码不再变化,终止
current = next;
} while (true);
return current;
}
}</errors></errors></errors>
⚠️ 注意事项:
- 避免无限循环:unescapeXml() 对非法实体(如 &xyz;)默认保留原样,因此循环终止条件必须基于字符串是否发生变化,而非单纯判断是否含 &。
- 性能考量:生产环境不建议无限制循环;若可预知最大转义层数(如≤3),推荐限定迭代次数(如 for (int i = 0; i
- 版本兼容性:该行为在 commons-lang3 3.4 至最新版(3.14.x)中保持一致;JDK 版本(如 11.0.16)不影响此逻辑。
- 替代方案:如需更严格的XML解析,建议使用 javax.xml.parsers.DocumentBuilder 或 org.jsoup.Jsoup.parse() 处理完整文档,而非手动解码片段。
总之,XML解码不是“一劳永逸”的操作——它要求开发者对数据来源的转义历史有清晰认知。精准控制解码次数,是保障内容语义正确性的关键。










