XPath查询XML文档的注意事项-JavaScript教程
转载自:互联网 作者:cd3c.com
您正在看的JavaScript教程是:XPath查询XML文档的注意事项。
老鼠与人最好的排列计划
本文的灵感来自一个诸事不顺的周末。我的那一位决定和同事去拉斯维加斯进行一次庆祝旅行,恰好我计划去宜家家居挑一个书柜,这样我在搬到雷得蒙几个月之后终于可以将我的书松绑了。在宜家家居逛了两个小时之后,我发现一个陈列的书柜与我房间的色调很相配,只是一些必要的配件缺货。最终我订购了这种书柜,空着手先回家了。然而,我已经把书都解开了,它们散落在房间的各个角落。这成为整理我日益增长的书库的最好机会,当然我选择使用 XML 实现此任务。(本文包含一些指向英文站点的链接。)
不仅是表面那么简单
我正在建造的 XML 分类的主要目的是建立一个结构来集中存储我所拥有的书的信息,它应该足够灵活,可以进行查询和各种演示,同时也便于携带。下面是该文档的初稿的摘录:
Books.xml
<?xml version="1.0" encoding="UTF-8" ?>
<bk:books xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf" on-loan="yes" >
<bk:book publisher="IDG books" on-loan="Sanjay" >
<bk:title>XML Bible</bk:title>
<bk:author>Elliotte Rusty Harold</bk:author>
</bk:book>
<bk:book publisher="QUE">
<bk:title>XML By Example</bk:title>
<bk:author>Benoit Marchal</bk:author>
</bk:book>
</bk:books>
我希望能够使用 on-loan 属性来跟踪我是否将书借了出去。根元素上的 on-loan 属性指定至少一本书被借了出去,然后将每个 book 元素上相同的属性指定借书人。现在看来,这可能不是最好的设计,因为它导致在根元素和子元素之间不必要的联系,但是请诸位谅解,这毕竟只是我的初稿。
设计好简单的格式后,我决定在上面运行一些实际的查询来看看这种格式是否满足我的需要。我尝试在 System.Xml.XmlNode 类中使用 SelectSingleNode Method 的第一个查询如下:
//*[position() = 1]/@on-loan
我本来的意思是“选择文档中的所有节点,然后给我第一个节点的 on-loan 属性。”查询返回以下内容:
on-loan="yes"
因此,对问题“我是否有书借出?”的答案是 yes。然而,当我模拟如果一本书被借出之后我没有更新根元素上的 on-loan 值会出现什么情况时发生了一件有趣的事情。我从根元素中删除 on-loan 属性,再次运行查询。结果如下:
on-loan="Sanjay"
结果是根元素的某个子元素的值。我怀疑有错,所以又在 MSXML 上尝试查询,还是得到相似的结果。进一步研究使我与小组内的一些 XPath 专家进行了很有启发意义的讨论,并进一步阅读了 XPath 建议。我发现与多方共同设计的重要语言一样,有一些古怪、富有个性及不一致的情况,(即在处理 XPath 时,需要避免的陷阱)。
缩写及它们的真实意思
XPath 建议列出了一些轴,这些轴包含了一些与当前选择节点(也称为上下文节点)相关的节点。为避免冗长,指定了某些常用轴的一些缩写。下表显示这些缩写和它们等效的轴。
缩写
轴
.
self::node()
..
parent::node()
//
/descendent-or-self::node()/
@
attribute::
另一个事实是在每个位置步骤或路径表达式上使用的默认轴是 child:: 轴。因此,/bk:books/bk:book 实际等效于 /child::bk:book/child::bk:book, 这比直接键入要容易得多。
节点测试用来选择当前轴的主节点类型的所有节点。* 是节点测试,不是步骤的缩写。最后,带有数字的谓词等效于检查上下文节点的位置是否与该数字相同。这意味着查询 /bk:book[1] 等效于 /bk:book[position()=1]。
有了以上信息,我们可以返回原来出问题的查询,看看为什么会得到意外的结果。//*[position() = 1]/@on-loan 实际是 /descendent-or-self::node()/child::*[position() = 1]/@on-loan 的缩写,它选择文档中的每个节点,检索“每个选定节点的第一个子节点”的 on-loan 属性。明智地使用圆括号可以迅速解决此问题,(//*)[position() = 1]/@on-loan(它是 (/descendent-or-self::node()/child::*)[position() = 1]/@on-loan 的缩写) 是我实际想要的。
有趣的是,在问题解决之后不久,我意识到实现我要求的更简单和更有效的查询本来可以是:
/*/@on-loan
这是一个更好的解决方案,因为它只需要查看文档中的第一个节点。我将保留多个示例,强调为什么一个人应该考虑在某些情况下缩写所表示的内容,以避免令人迷惑不解的结果。
缩写
完整查询
查询结果
//*[1]
/descendent-or-self::node()/child::*[position()=1]
选择文档中每个节点的第一个子节点。
(//*)[1]
(/descendent-or-self::node()/child::*)[position()=1]
选择文档中第一个节点。
提高我们的数学技能
涉及关系或算术运算符和字符串的查询通常导致与直觉不相符的结果。XPath 将涉及关系或算术运算符的表达式中的所有操作数转换为数字。不完全是数字值的字符串将转换为 NaN(不是一个数)。下表显示某些 XPath 表达式、表达式隐式转换成的内容以及表达式的结果。
表达式
隐式转换
结果
'5' + 7
5 + 7
12
'5' + '7'
5 + 7
12
5 + 'a'
5 + NaN
NaN
'5' < 7
5 < 7
True
'5' < '7'
5 < 7
True
'5' < 'b'
5 < NaN
False
'a' < 'b'
NaN < NaN
False
'a' > 'b'
NaN > NaN
False
必须注意到比较运算符(<、>、<=、>=)不执行字符串值的字典式比较功能。
另一个有趣的算术定义是虽然定义了一元减号(例如 -6 是有效的 XPath 表达式),但是却未定义一元加号(+6 不是有效的 XPath 表达式)。更令人吃惊的是多重否定可以堆叠在一起,却仍然有效。因此,------6 是有效的 XPath 表达式,等效于值 6。
XPath 缺乏对科学/指数记数法的支持将使用户犯错,因为支持它的既有流行的查询语言(如 SQL),也有流行的编程语言(如 C++)。
在节点集合上结合算术和关系运算的表达式还可能导致令人吃惊的结果。节点集合上的算术运算将“集合中第一个节点”的值转换为数字,而关系运算符将判断“节点集合中的任意节点”是否满足条件。下面是一个 XML 文档,用来显示算术运算和关系运算符如何导致不 Associative(结合)的表达式。
Numbers.xml
<Root>
<Numbers>
<Integer value="4" />
<Integer value="2" />
[1] [2] [3] 下一篇
