Node.js版本:10.15.3 jsdom版本:15.1.0
const fs = require('fs');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const xmlFile = fs.readFileSync("question.xml", "utf8");
const dom = new JSDOM(xmlFile);
const all = dom.window.document.querySelectorAll("S");
console.log(all);
<?xml version="1.0" encoding="utf-8"?>
<Foo>
<FooBar>
<S a="string1" b="string2" c="string3"/>
</FooBar>
</Foo>
<Foo>
<FooBar>
<S a="string1" b="string2"/>
<S a="string1"/>
</FooBar>
</Foo>
querySelectorAll("S")
仅在明显只有3个时返回7个HTML元素。更奇怪的是,如果我将xml元素从S
重命名为F
,它可以正常工作并且{{ 1}}仅找到3个元素。这种不一致的原因是什么?
答案 0 :(得分:1)
默认情况下,JSDOM将您赋予它的标记解释为HTML。因此,它将XML解释为HTML,从而获得了时髦的结果。请记住,HTML规范提供了有关如何理解损坏的HTML的规则,因此当JSDOM读取您的XML时,它将应用规则并尝试从中获取一些明智的文档。如果我使用您的XML和您的代码,但我添加
console.log(dom.window.document.documentElement.innerHTML);
在分配dom
的行之后,我得到了这个序列化的HTML:
<head></head><body><foo>
<foobar>
<s a="string1" b="string2" c="string3">
</s></foobar><s a="string1" b="string2" c="string3">
</s></foo><s a="string1" b="string2" c="string3">
<foo>
<foobar>
<s a="string1" b="string2">
<s a="string1">
</s></s></foobar><s a="string1" b="string2"><s a="string1">
</s></s></foo><s a="string1" b="string2"><s a="string1">
</s></s></s></body>
看看s
发生了什么。 (提醒:HTML元素名称不区分大小写,因此S
和s
是相同的HTML元素。)
顺便说一句,您通过与S
相反而与F
得到不同行为的原因是因为S
是actual HTML element,而F
不是。因此,当JSDOM试图将您的文档理解为HTML时,它们对S
和F
的使用不同的规则。
为了使JSDOM将您的文档解释为XML,可以执行以下操作:
const dom = new JSDOM(xmlFile, { contentType: "text/xml" });
但是请注意,您的文档不是格式正确的XML,因为它具有多个根元素。 XML规范不提供任何规则来理解格式不正确的文档。格式不正确的文档本质上不是XML。因此,JSDOM只会拒绝您的文档。您需要对其进行编辑,以使其仅具有一个根元素。例如,这将起作用:
<?xml version="1.0" encoding="utf-8"?>
<doc>
<Foo>
<FooBar>
<S a="string1" b="string2" c="string3"/>
</FooBar>
</Foo>
<Foo>
<FooBar>
<S a="string1" b="string2"/>
<S a="string1"/>
</FooBar>
</Foo>
</doc>
我刚刚将两个Foo
元素包装在doc
元素中,该元素构成了XML所需的单个根。