使用浏览器内置DOMParser解析XML时,doctype.entities为何始终为空?
为什么DOMParser不加载XML实体到doctype.entities?
兄弟,我之前也踩过这个坑!你遇到的情况是浏览器环境下DOMParser的预期行为,核心原因有两个:
- 安全优先级高于规范:浏览器为了防范实体注入类的安全风险(比如XXE漏洞),默认会主动忽略DOCTYPE里定义的内部实体,不会把它们填充到
DocumentType.entities这个NodeMap中。毕竟如果随便暴露实体定义,很可能被恶意利用来读取本地文件或者发起恶意请求。 - DOM规范的浏览器实现差异:虽然DOM Level 2规范明确说
DocumentType.entities应该包含文档中的实体,但大多数现代浏览器的DOMParser并没有严格遵循这一点——解析器会直接把实体引用替换成对应的值,而不会保留实体节点的引用,自然entities就空了。
先确认实体是否真的被解析了
虽然doctype.entities是空的,但你可以验证实体其实已经被正确解析了。比如把XML改成这样:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE tag1 [ <!ENTITY hello "hello1"> <!ENTITY there "there1"> ]> <tag1>&hello; &there;</tag1>
然后用你的代码测试:
function parseXml(xmlString) { var parser = new DOMParser(); var xmlDoc = parser.parseFromString(xmlString, "text/xml"); console.log(xmlDoc.querySelector('tag1').textContent); // 会输出 "hello1 there1" console.log(xmlDoc.doctype.entities); // 还是空的NodeMap }
这说明实体确实被解析替换了,只是没暴露在entities属性里而已。
如果需要获取实体定义怎么办?
如果你的需求是拿到实体的名称和对应值,而不是只用替换后的内容,那只能手动解析XML字符串里的DOCTYPE部分。用正则表达式就能搞定简单的内部实体:
function getXmlEntities(xmlString) { const entityMap = {}; // 先匹配DOCTYPE里的实体声明块 const doctypeContent = xmlString.match(/<!DOCTYPE[^[]+\[([^\]]+)\]>/s)?.[1]; if (!doctypeContent) return entityMap; // 提取每个实体的名称和值 const entityRegex = /<!ENTITY\s+(\w+)\s+"([^"]+)"/g; let match; while ((match = entityRegex.exec(doctypeContent)) !== null) { entityMap[match[1]] = match[2]; } return entityMap; } // 测试一下 const xmlString = `<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE tag1 [ <!ENTITY hello "hello1"> <!ENTITY there "there1"> ]> <tag1>Stuff in here</tag1>`; console.log(getXmlEntities(xmlString)); // 输出: { hello: "hello1", there: "there1" }
⚠️ 注意:这个方法只适用于简单的内部实体,如果你的XML有外部实体或者更复杂的声明(比如带参数的实体),可能需要写更完善的解析逻辑,或者考虑用专门的XML解析库。
内容的提问来源于stack exchange,提问作者Thierry
相关产品推荐
相关产品推荐

