在XML文档的实际使用中,我们常常需要嵌入非XML格式的文本,比如一段JavaScript代码、SQL语句或者包含大量标点符号的业务描述内容。这些文本中可能包含XML语法里的特殊字符,比如小于号、大于号、和号等,如果直接写入XML文档,解析器会把这些字符当成XML标签或者实体的一部分,进而导致文档解析报错,无法正常使用。CDATA就是XML规范提供的专门解决这类问题的机制,它可以让内部的内容被解析器当作普通文本处理,不会被解析成XML结构。

什么是CDATA
CDATA的全称是Character Data,也就是字符数据,它是XML文档中的一种特殊区块。在CDATA区块内部的所有文本,解析器都不会对其进行XML语法解析,而是直接当作原始字符处理。CDATA的语法格式是固定的,以<![CDATA[开头,以]]>结尾,中间的内容就是需要安全嵌入的非XML文本。
CDATA的基本语法规则
- 开头标记和结尾标记必须完整,不能缺少任何一个部分
- CDATA区块内部不能再嵌套其他的CDATA区块
- 结尾标记
]]>不能再出现在CDATA的内部内容中,否则会被当成区块结束 - CDATA可以放在XML元素的内部,不能作为XML标签的属性值使用
为什么需要用CDATA处理非XML文本
XML中有几个预定义的特殊字符,这些字符在XML语法里有特殊的含义,如果直接出现在文本内容中,必须先进行转义才能被正常解析,否则会破坏XML的结构。常见的特殊字符和对应的转义写法如下:
| 特殊字符 | XML中的含义 | 转义后的写法 |
|---|---|---|
| < | 标签开始符号 | < |
| > | 标签结束符号 | > |
| & | 实体引用开始符号 | & |
| ' | 属性值引号 | ' |
| " | 属性值引号 | " |
如果非XML文本中包含大量这类特殊字符,逐个转义会非常麻烦,还容易出错。比如下面这段JavaScript代码,如果直接放到XML元素里,小于号和大于号会导致解析错误:
function compare(a, b) {
if (a < b) {
return -1;
}
return 1;
}
如果用CDATA包裹这段代码,就不需要做任何转义,解析器会直接把整个区块的内容当作普通文本处理,既方便又不容易出错。
CDATA的使用示例
正确嵌入JavaScript代码到XML
下面是一个完整的XML示例,把上面的JavaScript代码用CDATA安全嵌入到script元素中:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<script>
<![CDATA[
function compare(a, b) {
if (a < b) {
return -1;
}
return 1;
}
]]>
</script>
</root>
嵌入SQL语句到XML配置
很多配置文件会用XML存储SQL语句,SQL语句中经常包含小于号、大于号、和号,用CDATA处理非常合适:
<?xml version="1.0" encoding="UTF-8"?>
<queries>
<query id="getUsers">
<![CDATA[
SELECT * FROM user WHERE age > 18 AND status = 'active'
]]>
</query>
</queries>
CDATA和常规转义方式的区别
CDATA和逐个转义特殊字符都能解决特殊字符冲突的问题,但是两者的适用场景不同:
- 如果文本内容中特殊字符很少,用转义方式更简洁,不会增加额外的标记
- 如果文本内容很长,或者包含大量特殊字符,比如代码片段、JSON字符串、HTML片段,用CDATA更方便,不需要逐个处理转义
- CDATA不能用于XML属性值,属性值中的特殊字符只能用转义的方式处理
比如下面的属性值场景,就只能转义,不能用CDATA:
<!-- 错误写法,CDATA不能放在属性里 --> <item desc="<![CDATA[测试<内容>]]>" /> <!-- 正确写法,用转义处理属性里的特殊字符 --> <item desc="测试<内容>" />
使用CDATA的注意事项
- CDATA的结尾标记是
]]>,如果嵌入的文本本身包含这个字符串,需要提前拆分处理,比如把]]>拆成]]和>分别放在两个CDATA区块中,或者用转义的方式处理这部分内容。 - CDATA区块可以放在XML元素的任意位置,但是不能放在XML声明之前,也不能放在处理指令内部。
- 虽然CDATA里的文本不会被解析,但是如果XML文档本身有编码设置,CDATA内部的内容还是要符合文档的编码规则,避免出现乱码。
CDATA是XML规范的一部分,所有符合XML标准的解析器都支持CDATA区块,不用担心兼容性问题,可以放心在需要嵌入非XML文本的场景中使用。