QuerySelectorAll JSDOM вернул слишком много элементов XML - PullRequest
0 голосов
/ 27 мая 2019

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") возвращает 7 элементов HTML, когда их явно только 3. Что делает его еще более странным, так это то, что если я переименую элементы xml с S на F, он будет работать правильно, а querySelectorAll("F") найдет только 3 элемента. В чем причина этого несоответствия?

1 Ответ

1 голос
/ 27 мая 2019

По умолчанию 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 является фактическим HTML-элементом , тогда как F - нет. Так что JSDOM применяет другие правила к S, чем F, когда пытается понять ваш документ как HTML.

Чтобы 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.

...