JSDOM的querySelectorAll返回太多XML元素

时间:2019-05-27 17:22:32

标签: node.js jsdom

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个元素。这种不一致的原因是什么?

1 个答案:

答案 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元素名称不区分大小写,因此Ss是相同的HTML元素。)

顺便说一句,您通过与S相反而与F得到不同行为的原因是因为Sactual HTML element,而F不是。因此,当JSDOM试图将您的文档理解为HTML时,它们对SF的使用不同的规则。

为了使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所需的单个根。