主流xml解析器默认忽略注释和处理指令,需显式启用保留模式:elementtree需自定义treebuilder,lxml支持parse_comments=true,dom需设置domconfig参数。

XML解析器默认忽略注释和处理指令
绝大多数主流XML解析器(如Python的xml.etree.ElementTree、Java的DOMParser、JavaScript的DOMParser)在构建文档树时,会直接跳过<!-- comment -->和<?pi target?>节点。这不是bug,而是W3C规范允许的“非信息项”处理策略——除非显式启用保留模式,否则它们不会出现在childNodes或iter()结果中。
ElementTree需用XMLParser(target=)捕获注释
Python标准库的ElementTree不提供开箱即用的注释访问接口,但可通过自定义TreeBuilder配合XMLParser实现。关键在于重写comment()和pi()方法:
from xml.etree import ElementTree as ET
from xml.etree.ElementTree import TreeBuilder
<p>class CommentPreservingBuilder(TreeBuilder):
def comment(self, data):
self.start(ET.Comment, {})
self.data(data)
self.end(ET.Comment)</p><pre class='brush:php;toolbar:false;'>def pi(self, target, data):
self.start(ET.PI, {'target': target, 'data': data})
self.end(ET.PI)parser = ET.XMLParser(target=CommentPreservingBuilder()) root = ET.parse("doc.xml", parser).getroot()
注释节点现在是真实元素,可用findall查找
comments = root.findall(".//{*}comment") # 注意命名空间通配
-
ET.Comment和ET.PI是特殊节点类型,不能用字符串标签名直接匹配 - 必须用
.//{*}comment这种带通配命名空间的XPath,否则findall("comment")找不到 - 注释内容通过
node.text获取,而非node.attrib
lxml支持parse_comments=True一键开启
如果你能引入第三方库,lxml是最省心的选择。它原生支持注释和PI节点保留,并提供清晰的API:
from lxml import etree
<p>parser = etree.XMLParser(remove_comments=False, recover=True)
tree = etree.parse("doc.xml", parser)
root = tree.getroot()</p><h1>直接遍历所有节点,包括注释</h1><p>for node in root.iter():
if isinstance(node, etree._Comment):
print("Comment:", node.text.strip())
elif isinstance(node, etree._ProcessingInstruction):
print("PI:", node.target, node.text)</p>-
remove_comments=False是必须参数,缺省为True -
etree._Comment和etree._ProcessingInstruction是具体类型,不能用字符串判断 - 注意
recover=True可容忍部分格式错误,避免解析中断
DOM解析中需手动设置domConfig特性
浏览器环境或Java DOM中,注释节点默认存在但可能被过滤。必须显式启用"comments"和"CDATA-sections"特性:
// JavaScript示例(浏览器环境)
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, "application/xml");
// 启用注释保留(部分浏览器需此步)
doc.domConfig?.setParameter("comments", true);
doc.domConfig?.setParameter("CDATA-sections", true);
<p>// 现在可以安全遍历
Array.from(doc.childNodes).forEach(node => {
if (node.nodeType === Node.COMMENT_NODE) {
console.log("Comment:", node.textContent);
}
});</p>- 现代Chrome/Firefox通常默认保留注释,但Safari和旧版IE可能需要
domConfig -
Node.COMMENT_NODE值为8,Node.PROCESSING_INSTRUCTION_NODE为7 - 服务端DOM(如JAXP)必须调用
setFeature("http://apache.org/xml/features/dom/include-comments", true)
注释和处理指令的映射不是“有没有”的问题,而是“要不要主动打开开关”的问题。不同解析器的默认行为差异极大,最容易踩的坑是:以为iter()或childNodes天然包含它们,结果调试半天发现根本没进循环。










