XML解析器默认将CDATA视为普通文本,无法在解析后区分其来源;ElementTree不支持CDATA识别,lxml或Java DOM需特殊处理;CDATA不保证内容安全,误用可能导致XSS。

XML解析器默认会把CDATA当成普通文本处理
绝大多数XML解析器(如Python的xml.etree.ElementTree、Java的DocumentBuilder、JavaScript的DOMParser)在读取CDATA节时,不会做特殊标记或保留其边界,而是直接提取内部原始字符串。这意味着hello]]>会被当作字符串"<tag>hello</tag>",和写成<tag>hello</tag>在DOM树中表现一致——你无法仅凭解析结果区分它原本是不是CDATA。
ElementTree中无法原生识别CDATA节
xml.etree.ElementTree根本不暴露CDATA信息:它没有回调、标志位或属性来指示某段文本来自CDATA。如果你依赖CDATA作为“内容不转义”的语义标记(比如存HTML片段),必须在序列化阶段手动包裹,或改用支持CDATA事件的解析器。
- 读取时:所有文本统一走
elem.text或elem.tail,无区别 - 写入时:
ElementTree默认不生成CDATA,需自行拼接字符串或换库 - 替代方案:用
lxml.etree,它提供etree.CDATA()包装器
from lxml import etree
root = etree.Element("script")
root.text = etree.CDATA("<div id='app'>{{msg}}</div>")
print(etree.tostring(root, encoding="unicode"))
# 输出: <script><![CDATA[<div id='app'>{{msg}}</div>]]></script>Java DOM中需用Node.CDATA_SECTION_NODE判断类型
标准org.w3c.dom API允许你在遍历时检查节点类型。只有显式调用getNodeValue()并确认getNodeType() == Node.CDATA_SECTION_NODE,才能知道当前文本来自CDATA节。
- 注意:
getTextContent()会合并所有子文本,丢失CDATA来源信息 - 若用
Transformer输出,需设置OutputKeys.CDATA_SECTION_ELEMENTS指定哪些元素内容应被包裹为CDATA - 常见错误:用
element.getTextContent()后直接映射,导致嵌套标签被误解析为结构而非字符串
JSON映射时CDATA内容通常要转义或加标记字段
XML转JSON没有标准规范,而CDATA的核心语义是“保持原样不解析”,这在JSON里没有对应概念。常见做法有三种:
- 直接保留为字符串字段,但前端需约定该字段值为“已预转义HTML”(例如
{"content": "<p>Hello</p>"}) - 增加元字段标识,如
{"content": "<p>Hello</p>", "_cdata": true} - 对含HTML的字段统一用
html后缀命名,如description_html,形成隐式约定
最容易被忽略的是:服务端若把CDATA内容当作纯文本返回给前端,而前端又用innerHTML渲染,就可能触发XSS——因为CDATA本身不提供安全保证,只是绕过XML解析器的转义,不代表内容可信。










