Синтаксис XPath с суффиксом /text() или без него

На разных веб-сайтах предоставленный синтаксис XPath отличается, в первую очередь необходимостью суффикса "/text()".

Цитируя синтаксис без суффикса:

Ссылаясь на синтаксис с необходимостью суффикса:

Насколько мне известно, разные библиотеки тоже работают только с суффиксом или без него (раньше не встречал такой, которая работает и с суффиксом, и без).

Не требует суффикса:

Требуется суффикс:

  • Собственная реализация XPath для Java JRE

Казалось бы, скорее всего, есть разница между реализацией библиотеки XPath, предназначенной для использования с XML, и для использования с DOM? Если да, то в чем разница и где я могу найти разницу?


person Jasper Teng    schedule 15.08.2019    source источник
comment
Как будто я вижу, как некоторые люди едят вилками, а некоторые ложками. Что правильно? Зависит от того, едите вы суп или стейк. Если вам нужны текстовые узлы, вы используете text(). Если вы не смотрите на текстовые узлы, вы этого не сделаете. Дело не в правильности, требованиях или реализациях, а в том, что нужно коду. Вы можете узнать о разнице, изучая XML, DOM и XPath (не из фрагментов, а из фактической документации XPath, например MDN).   -  person Amadan    schedule 15.08.2019


Ответы (2)


Я думаю, что вы неправильно диагностировали ситуацию, и причина неправильного диагноза (если провести аналогию слишком далеко) заключается в том, что вы рассмотрели симптомы примерно 7 пациентов, а не пошли в медицинский институт и не изучали анатомию.

«Анатомия» здесь — это модель данных XDM, которая лежит в основе семантики XPath. Обратите внимание, в частности, что

(а) когда у вас есть такая структура

<title>Water</title>

есть узел элемента, строковое значение которого равно «Вода» и который является родителем одного текстового узла, строковым значением которого также является «Вода».

(b) когда у вас есть такая структура

<title>H<sub>2</sub>O</title>

есть узел элемента со строковым значением "H2O", который является родителем трех дочерних элементов: текстовый узел со строковым значением "H", узел элемента со строковым значением "2" (который сам является родителем другого текстового узел...) и второй текстовый узел со строковым значением "O".

В случае (а) почти все операции дают одинаковый результат независимо от того, применены ли они к узлу элемента или узлу текста. Например, contains($x, "ate") будет истинным, независимо от того, является ли $x узлом элемента или текстовым узлом. Так что добавление /text() к пути вообще избыточно: не вредит, но и не нужно. Мы часто советуем не делать этого, потому что это делает ваш код более хрупким, если структура данных позже изменится, не говоря уже о простом добавлении ненужного многословия.

В случае (b) добавление /text() к вашему пути приводит к тому, что вы выбираете два текстовых узла «H» и «O» вместо выбора узла элемента. В XPath 1.0 многие операции (например, contains()) при применении к последовательности из двух текстовых узлов игнорируют все, кроме первого, поэтому contains(x/y/title/text(), "O") возвращает false; в XPath 2.0 выдается ошибка, говорящая о том, что аргумент для contains() должен быть одноэлементным. Если вы просто хотите узнать, содержит ли заголовок букву «О», то гораздо лучше пропустить /text() и применить операцию к строковому значению элемента, что является конкатенацией всех текстовых узлов.

Единственный раз, когда вам нужно использовать «/text()», это если вы хотите более глубоко изучить внутреннюю структуру элемента title.

Конечно, возможно, что между реализациями XPath есть различия — не все из них на 100% соответствуют стандарту. Но основные реализации довольно совместимы, и если вы обнаружите разницу, пожалуйста, сообщите нам об этом: четко укажите исходный документ, выражение пути и разные результаты, полученные в разных реализациях.

person Michael Kay    schedule 15.08.2019

Если вы посмотрите на соответствующие спецификации, то обнаружите, что оба XPath 1.0 https://www.w3.org/TR/xpath-10/#node-tests, а также спецификацию XPath 2.0 https://www.w3.org/TR/xpath20/#node-tests определяют то, что вы называете "суффиксом", как "тест узла", text() используемый для выберите любой «текстовый узел».

Ни одна из спецификаций не делает использование text() обязательным, но, конечно, это опция, которая есть в языке и должна выбирать текстовые узлы, например, со смешанным содержимым элементов и текста и/или комментариев, когда у вас есть причина только для выбора дочерние текстовые узлы.

Что касается реализаций, я не думаю, что реализация Java XPath 1.0 требует от вас ее использования, единственная причина, по которой некоторые старые коды, специфичные для DOM, используют foo/text() вместо простого foo, чтобы затем прочитать содержимое строки внутри элемента, например. <foo>some example</foo> заключается в том, что в более старых реализациях DOM, если вы выбираете узел Element, у вас нет свойства или метода для доступа к текстовому содержимому элемента в виде строки, поэтому люди использовали foo/text() для выбора дочернего узла Text узла Element, а затем могли использовать свойство nodeValue (Javascript) или метод getNodeValue() (Java), чтобы получить строку с some example. Однако в течение многих лет DOM предоставляет свойство textContent для Element узлов, поэтому в наши дни вы можете использовать foo в качестве XPath, получить узел Element и прочитать textContent или getTextContent() соответственно, чтобы получить строку some example.

MSXML DOM и XPath также довольно старые и никогда не обновлялись до спецификации DOM Level 3 W3C, но у Microsoft с самого начала было собственное свойство .text для узлов элементов, которое вы можете использовать там вместо стандартизированного textContent. Тем не менее, в этом контексте я видел аналогичные попытки явного считывания foo/text() в виде списка узлов, в котором вы затем можете получить доступ к nodeValue каждого текстового узла в виде строки.

Единственное «предпочтение» реализации для использования foo/text() вместо foo, которое я видел, находится в библиотеке Python lxml, если вы хотите прямое сопоставление выбора XPath со списком строк Python, в этом случае выражение, подобное foo/text() в контексте, например. <data><foo>a</foo><foo>b</foo></data> даст вам на стороне Python список из двух строк Python с a и b, а использование foo даст вам список с двумя узлами элементов. Таким образом, в зависимости от ваших потребностей на стороне основного языка в этом случае может быть проще использовать foo/text(), но вы должны знать, что ввод, подобный <data><foo>a<!-- comment -->b</foo><foo>c</foo></data>, даст вам список с тремя строками.

person Martin Honnen    schedule 15.08.2019