5.3 XPath与XQuery:按图索骥


5.3 XPath 与 XQuery:按图索骥

XPath 是 XML 文档的寻址语言,用路径表达式在树中选取节点集;XQuery 建立在 XPath 之上,用 FLWOR 结构做跨文档的检索、过滤与结果构造。XSLT 的 select、XSD 的 key 引用、浏览器里的节点查找,底层全是 XPath——学会它,等于拿到所有 XML 工具的通用坐标。

XPath 路径:从绝对到谓词

沿用 inventory 文档。基础轴与缩写:/ 根下、// 全体后代、. 当前、.. 父级、@ 属性。

表达式 命中结果 /inventory/book/title 两个 title 元素(绝对路径) //stock 全文档所有 stock,无视层级 /inventory/book[@sku='9787111407010'] 谓词过滤:命中第一本书 //book[stock < 10]/title 库存低于10的书名 → JavaScript DOM编程艺术 //book[2] 第二个 book(位置下标从1起) //book[position() > 1] 除第一本外的所有 book /inventory/book/title/text() title 的文本节点而非元素 count(//book) 函数:2

六个语法要点藏在上面:谓词用方括号;比较符在 XML 里要写成 < > 转义(或用 <= 的替身写法);下标从 1 开始;// 慎用于大文档(全文扫描);text() 与元素本身有别;函数库包含 count、sum、contains、starts-with、name 等几十个。XPath 2.0 起序列、正则、for 表达式加入,能力逼近一门小查询语言。

带命名空间的 XPath:最疼的一跤

4.2 节说过前缀只是文档内别名,XPath 的规则由此而来:表达式里的前缀必须由"外界"注册,与文档前缀名无关。对一份用默认命名空间的文档直接查 //book,命中数为零——因为表达式里的 book 是"无命名空间的 book",而文档里的 book 挂在 URI 下。修法(以 Java 为例):

XPathFactory xf = XPathFactory.newInstance(); XPath xp = xf.newXPath(); xp.setNamespaceContext(new NamespaceContext() { public String getNamespaceURI(String prefix) { if (prefix.equals("v")) return "http://wh.example.org/v1"; return null; } /* 省略其余接口方法 */ }); NodeList hits = (NodeList) xp.evaluate( "/v:inventory/v:book", doc, XPathConstants.NODESET); // 表达式前缀 v 由本方注册指向该 URI;文档内叫什么前缀、或用默认声明,都不影响

排错口诀:查不到节点先问命名空间——九成"表达式明明对却命中零"的事故在这里。

XQuery:FLWOR 出场

XPath 定位"一份文档里的节点",XQuery 处理"跨文档检索与构造新文档"。骨架是 FLWOR 五个子句——for、let、where、order by、return:

xquery version "1.0"; <lowStock> { for $b in doc("inventory.xml")//book (: for:遍历候选节点 :) let $v := $b/stock (: let:绑定中间值 :) where $v < 20 (: where:过滤 :) order by $v ascending (: order:排序 :) return <alert sku="{$b/@sku}">{$b/title} 仅剩 {$v} 本</alert> } (: return:构造结果节点 :) </lowStock>

对样例文档执行,输出:

<lowStock> <alert sku="9787115428028">JavaScript DOM编程艺术 仅剩 7 本</alert> </lowStock>

结果本身就是新 XML——XQuery 不只查,还造。这让它成为原生 XML 数据库(6.2 节)的主力查询语言,也常被称作"XML 界的 SQL"。与 SQL 的关键差异:结果无固定行列概念,任意嵌套的文档都可以是输出形状。

⚠️ 性能习惯两条:谓词里能用具体路径就别 // 全文扫;跨文档大检索交给 XQuery 引擎(有索引),别在宿主语言里循环 evaluate 单条 XPath(每次都有解析开销)。

表达式调优的两则手感

讲两则实战手感收尾本节。其一,谓词前置优于全树扫描//book[stock < 10] 在大文档上要扫全部后代找 book,写成 /inventory/book[stock < 10] 沿确定路径下钻,代价天差地别。// 留给"确实不知道节点在哪层"的场景,且这种"不知道"本身往往说明文档结构该沟通一下了。其二,一次取全优于循环单取:在宿主语言里循环调用单条 XPath 取每个 book 的 title,每次调用都有表达式编译开销;改成一条 XPath 取回节点集再在宿主语言里遍历,速度常有一个量级的差距。两则手感共同指向一个心智:XPath 是发给引擎的查询,不是循环体内的一行取值——把它当 SQL 用,而不是当数组下标用。这个心智换到 XQuery 上同样成立:能在 FLWOR 里一个 for 完成的分组统计,就别拆成宿主语言的循环加单条查询。查询语言的效率来自让引擎看到"全貌",看到全貌它才能优化;逐条喂食等于蒙住引擎的眼睛让它跑。

本节要点回顾

  • XPath 是通用坐标:XSLT、XSD、各类库共用,学一次处处用;
  • 谓词、下标从 1、比较符转义是三个立即要会的细节;
  • 带命名空间必须注册前缀,命中为零先查 URI 绑定;
  • XQuery = XPath + FLWOR,能查能造,原生数据库主力;
  • 性能:少用 //,跨文档检索交给有索引的引擎。

加工流水线到此全线贯通。第 6 章进入特种车间:解析器与查询引擎面对恶意输入和海量存储时的两场硬仗。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U