XML作为一种常用的数据交换格式,有严格的语法约束,其中特殊字符和实体引用的处理是开发过程中必须掌握的基础内容,处理不当很容易引发解析失败或者数据内容错误的问题。

XML中需要转义的特殊字符
XML解析器在解析文件时,会把特定字符作为语法标记识别,因此这些字符如果出现在元素内容、属性值等位置,必须进行转义处理,否则会破坏XML的结构合法性。常见的需要转义的特殊字符如下:
| 原始字符 | 转义后的实体引用 | 说明 |
|---|---|---|
| < | < | 小于号,XML标签的起始标记,直接出现会被误认为标签开始 |
| > | > | 大于号,XML标签的结束标记,直接出现会被误认为标签结束 |
| & | & | 与符号,实体引用的起始标记,直接出现会被误认为实体开始 |
| " | " | 双引号,属性值的包裹符号,直接出现在属性值中会导致属性边界错误 |
| ' | ' | 单引号,属性值的包裹符号,直接出现在属性值中会导致属性边界错误 |
XML实体引用的分类
XML中的实体引用分为预定义实体和自定义实体两类,预定义实体是XML规范内置的,自定义实体需要开发者在文档中自行定义后使用。
预定义实体
就是上表中列出的五个转义对应的实体,无需额外定义,直接在XML内容中使用即可。比如元素内容中包含小于号时,写法如下:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<content>数值比较:3 < 5</content>
</root>
自定义实体
如果某些内容在XML中频繁出现,或者包含大量需要转义的字符,可以定义自定义实体来简化书写。自定义实体需要在XML文档的<!DOCTYPE>声明中定义,语法格式为<!ENTITY 实体名称 "实体内容">。示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY company "示例科技公司">
<!ENTITY address "北京市海淀区测试路1号">
]>
<root>
<company_name>&company;</company_name>
<company_address>&address;</company_address>
</root>
解析上述XML时,&company;会被替换为示例科技公司,&address;会被替换为北京市海淀区测试路1号。
不同场景下的处理实现
Java中处理XML特殊字符
Java中可以使用StringEscapeUtils工具类完成XML特殊字符的转义和反转义,也可以借助DOM、SAX等解析器自动处理实体引用。转义示例代码如下:
import org.apache.commons.text.StringEscapeUtils;
public class XmlSpecialCharDemo {
public static void main(String[] args) {
// 原始内容包含特殊字符
String originalContent = "测试内容:a < b & c > d";
// 转义XML特殊字符
String escapedContent = StringEscapeUtils.escapeXml11(originalContent);
System.out.println("转义后内容:" + escapedContent);
// 反转义XML特殊字符
String unescapedContent = StringEscapeUtils.unescapeXml(escapedContent);
System.out.println("反转义后内容:" + unescapedContent);
}
}
Python中处理XML特殊字符
Python的xml.sax.saxutils模块提供了escape和unescape方法,专门用于处理XML特殊字符的转义和反转义,示例代码如下:
from xml.sax.saxutils import escape, unescape
# 原始内容包含特殊字符
original_content = "测试内容:a < b & c > d"
# 转义XML特殊字符,默认转义<、>、&
escaped_content = escape(original_content)
print("转义后内容:" + escaped_content)
# 反转义XML特殊字符
unescaped_content = unescape(escaped_content)
print("反转义后内容:" + unescaped_content)
JavaScript中处理XML特殊字符
JavaScript中可以通过正则替换的方式实现XML特殊字符的转义,也可以利用DOM解析器处理实体引用,转义示例代码如下:
function escapeXml(xmlContent) {
return xmlContent.replace(/[&<>"']/g, function(match) {
switch(match) {
case '&': return '&';
case '<': return '<';
case '>': return '>';
case '"': return '"';
case "'": return ''';
default: return match;
}
});
}
function unescapeXml(xmlContent) {
return xmlContent.replace(/&|<|>|"|'/g, function(match) {
switch(match) {
case '&': return '&';
case '<': return '<';
case '>': return '>';
case '"': return '"';
case ''': return "'";
default: return match;
}
});
}
// 测试
let original = "测试内容:a < b & c > d";
let escaped = escapeXml(original);
console.log("转义后:" + escaped);
let unescaped = unescapeXml(escaped);
console.log("反转义后:" + unescaped);
注意事项
- 转义时只需要处理上述五个特殊字符即可,其他字符不需要额外转义。
- 自定义实体的定义必须放在XML文档的DOCTYPE声明中,且要在根元素之前定义,否则解析会报错。
- 如果使用解析器读取XML,大部分解析器会自动处理实体引用的替换,不需要手动反转义,只有手动拼接XML字符串时才需要主动处理转义。
- CDATA段可以包裹不需要转义的内容,CDATA段中的内容会被解析器当作纯文本处理,不会解析其中的特殊字符和实体引用,语法为<![CDATA[内容]]>,适合包裹大段的包含特殊字符的内容。
CDATA段的使用示例:<content><![CDATA[测试内容:a < b & c > d]]></content>,这段内容中的小于号、与符号、大于号都会被当作普通文本处理,不需要转义。