JSDOM 的 querySelectorAll 返回了太多 XML 个元素

JSDOM's querySelectorAll returned too many XML elements

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") returns 7 个 HTML 元素,而显然只有 3 个元素。更奇怪的是,如果我将 xml 元素重命名为 SF,它工作正常并且 querySelectorAll("F") 只找到 3 个元素。这种不一致的原因是什么?

默认情况下,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 元素名称是 case-insensitive 所以 Ss 是相同的 HTML 元素。)

顺便说一下,SF 不同的原因是因为 Sactual HTML element,而 F不是。因此,当 JSDOM 试图将您的文档理解为 HTML.

时,它对 S 应用的规则不同于 F

为了让 JSDOM 将您的文档解释为 XML,您可以这样做:

const dom = new JSDOM(xmlFile, { contentType: "text/xml" });

但请注意,您的文档不是 well-formed XML,因为它有多个根元素。 XML 规范没有提供任何规则来理解非 well-formed 的文档。不是 well-formed 的文档本质上不是 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.

所需的单个根