【问题标题】:XPath syntax, with or without "/text()" suffixXPath 语法,带或不带“/text()”后缀
【发布时间】:2019-12-21 14:59:32
【问题描述】:

不同网站提供的XPath语法不同,主要是需要"/text()"后缀。

引用语法无需后缀:

引用语法with需要后缀:

据我所知,不同的库也只能使用或不使用后缀(在此之前我没有遇到过同时使用和不使用后缀的库)。

不需要后缀:

需要后缀:

  • Java JRE 本机 XPath 实现

看起来,用于 XML 和用于 DOM 的 XPath 库实现之间很可能存在差异? 如果是这样,有什么区别,我在哪里可以找到区别?

【问题讨论】:

  • 这就像“我看到有些人用叉子吃饭,有些人用勺子吃饭。哪个是正确的?”看你吃的是汤还是牛排。如果您需要文本节点,请使用text()。如果您不查看文本节点,则不会。这与正确性或要求或实现无关,它只是关于代码需要什么。您可以通过了解 XML、DOM 和 XPath(不是来自 sn-ps,而是来自实际的 XPath 文档,例如 MDN's)来了解它们的区别。

标签: xml xpath xpath-2.0 domxpath xpath-1.0


【解决方案1】:

我认为您误诊了这种情况,误诊的原因是您查看了大约 7 名患者的症状,而不是去医学院学习解剖学。

这里的“剖析”是支持 XPath 语义的 XDM 数据模型。特别注意

(a) 当你有这样的结构时

<title>Water</title>

有一个元素节点,其字符串值为“Water”,是单个文本节点的父节点,其字符串值为“Water”。

(b) 当你有这样的结构时

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

有一个元素节点,其字符串值为“H2O”,它是三个孩子的父节点:一个字符串值为“H”的文本节点,一个字符串值为“2”的元素节点(它本身就是父节点)另一个文本节点...),以及字符串值为“O”的第二个文本节点。

在情况 (a) 中,无论是应用于元素节点还是文本节点,几乎所有操作都会产生相同的结果。例如,contains($x, "ate") 将是 true,无论 $x 是元素节点还是文本节点。所以在路径中添加/text() 通常是多余的:它没有害处,但没有必要。我们经常建议不要这样做,因为如果以后数据结构发生变化,它会使您的代码更加脆弱,而不仅仅是增加不必要的冗长。

如果 (b) 在路径中添加 /text() 会导致您选择两个文本节点“H”和“O”,而不是选择元素节点。在 XPath 1.0 中,许多操作(例如 contains())在应用于两个文本节点的序列时会忽略除第一个以外的所有操作,因此 contains(x/y/title/text(), "O") 将返回 false;在 XPath 2.0 中,它会抛出一个错误,指出 contains() 的参数必须是单例。如果你只是想知道标题是否包含字母“O”,那么最好省略/text(),并将操作应用于元素的字符串值,即所有文本节点的串联。

您需要使用“/text()”的唯一情况是您想更深入地探究title 元素的内部结构。

当然,XPath 实现之间可能存在差异——并非所有实现都 100% 符合标准。但是主流的实现还是很兼容的,如果你发现了不同之处,请告诉我们:源文档,路径表达式,以及不同实现得到的不同结果。

【讨论】:

    【解决方案2】:

    如果您查看相关规范,您会发现 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 然后读出字符串中的内容例如的元素&lt;foo&gt;some example&lt;/foo&gt; 是在较旧的 DOM 实现中,如果选择 Element 节点,则没有属性或方法可以将元素的文本内容作为字符串访问,因此人们使用 foo/text() 来选择 Text 子节点Element 的节点,然后可以使用 nodeValue 属性 (Javascript) 或 getNodeValue() 方法 (Java) 来获取带有 some example 的字符串。然而,多年来 DOM 在 Element 节点上提供了一个属性 textContent,所以现在,您可以使用 foo 作为 XPath 并获取一个 Element 节点并分别读出 textContentgetTextContent() 以获得字符串some example

    MSXML DOM 和 XPath 也相当陈旧,从未更新到 DOM Level 3 W3C 规范,但微软从一开始就在元素节点上拥有自己专有的 .text 属性,您可以在那里使用而不是标准化的 @987654343 @。尽管如此,在这种情况下,我已经看到类似的尝试将foo/text() 显式读出为节点列表,然后您可以在该列表上以字符串的形式访问每个文本节点的nodeValue

    如果您想将 XPath 选择直接映射到 Python 字符串列表(在这种情况下为表达式),我见过的唯一使用 foo/text() 而不是 foo 的实现特定“首选项”是在 Python 的 lxml 库中像 foo/text() 在例如的上下文中&lt;data&gt;&lt;foo&gt;a&lt;/foo&gt;&lt;foo&gt;b&lt;/foo&gt;&lt;/data&gt; 将在 Python 端为您提供两个 Python 字符串的列表,其中包含 ab,而使用 foo 将为您提供一个包含两个元素节点的列表。因此,在这种情况下,根据您在宿主语言方面的需求,使用foo/text() 可能会更容易,但您需要注意像&lt;data&gt;&lt;foo&gt;a&lt;!-- comment --&gt;b&lt;/foo&gt;&lt;foo&gt;c&lt;/foo&gt;&lt;/data&gt; 这样的输入将为您提供一个包含三个字符串的列表。

    【讨论】: