of {$slidecount} ½ {$title} ATZJG.NET {$author}

首页






关于 XML 的查询语言

XPath
XQuery
XSLT



Haifeng Xu


(hfxu@yzu.edu.cn)

This slide is based on Jeffrey D. Ullman's work, which can be download from his website.

References: http://www.w3school.com.cn/xpath/

目录

XPath/XQuery 数据模型

XPath/XQuery 数据模型

XPath 是 XQuery 的一个子集.

对应到关系模型的基本“关系”是: sequence of items.

一个 项(item) 是如下二者之一:

  1. 基本类型的值, 如: 整数、实数、布尔值或字符串.
  2. 一个节点(node).

关于在线测试 XQuery, 可以访问 http://www.xpathtester.com/xquery

节点的主要类型

节点的主要类型

  1. 文档节点(document node): 表示整个 XML 文档.
  2. 元素节点(element): 是 XML 元素, 包括它们的开始标签、配对结束标签(如果有的话), 以及在开始标签和结束标签之间的所有内容.
  3. 属性节点(attribute): 位于开始标签中.

文档节点

文档节点

形如 doc(URL)document(URL).

例如: doc(/usr/class/cs145/bars.xml)

所有 XPath(及 XQuery)查询都指向某个文档节点, 或是明确或是隐含的.

运行例子的 DTD

运行例子的 DTD

<!DOCTYPE BARS [
  <!ELEMENT BARS (BAR*, BEER*)>
  <!ELEMENT BAR (PRICE+)>
    <!ATTLIST BAR name ID #REQUIRED>
  <!ELEMENT PRICE (#PCDATA)>
    <!ATTLIST PRICE theBeer IDREF #REQUIRED>
  <!ELEMENT BEER EMPTY>
    <!ATTLIST BEER name ID #REQUIRED>
    <!ATTLIST BEER soldBy IDREFS #IMPLIED>
]>

例子: Document

例子: Document

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> 
    ...
</BARS>

Nodes as Semistructured Data

Nodes as Semistructured Data

XML 文档中的路径

XML 文档中的路径

XPath 是描述 XML 文档中路径的一种语言.

路径的描述是用一列 item 来表示的. 即所谓的路径表达式. 通常从根节点开始.

路径表达式(Path Expressions)

路径表达式(Path Expressions)

简单的路径表达式是用斜杠和标签组成的一个序列, 以 / 开始.

从文档节点开始, 然后从左到右依次构建这个路径.

Evaluating a Path Expression

Evaluating a Path Expression

路径表达式从根节点开始, 沿着一条具体路径(标签的序列/T1/T2/.../Tn)找到文档中的所有元素Tn.


以下是翻译, 可能有翻译不正确的地方.

例子: /BARS

例子: /BARS

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR> ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

/BARS 所代表的只有一项, 即整个 BARS 元素.

例子: /BARS/BAR

例子: /BARS/BAR

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

/BARS/BAR, 代表 <BARS> ... </BARS> 中的所有 BAR 子元素.

例子: /BARS/BAR/PRICE

例子: /BARS/BAR/PRICE

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

/BARS/BAR/PRICE, 代表了所有 <BAR> ... </BAR> 元素中的所有 PRICE 子元素.

路径中的属性(Attributes in Paths)

路径中的属性(Attributes in Paths)

有时候用户想找的不是元素, 而是元素的某个属性的值.

在属性名前面加一个 @ 符号, 表示这是属性.

例子: /BARS/BAR/PRICE/@theBeer

例子: /BARS/BAR/PRICE/@theBeer

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

/BARS/BAR/PRICE/@theBeer, 代表了所有 <PRICE> 元素中属性名为 theBeer 的值. 即得到的结果是 "Bud Miller", 以及包括其他 BAR 元素中 PRICE 子元素中 theBeer 属性的值.

Remember: Item Sequences

Remember: Item Sequences

到现在为止, 所有的 item sequences 都是元素的序列.

当路径表达式以属性结束, 则所得的结果是一些值组成的序列, 如上一个例子中的字符串序列.

可以从任何节点开始的路径

可以从任何节点开始的路径

若路径表达式从文档节点开始并以 //X 结束, 则第一步是从根标签或是从根标签的任意一个子元素开始寻找, 直到找到所有的 X 标签.

例子: //PRICE

例子: //PRICE

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

该路径也可找到所有的 PRICE 元素.

有用的路径表达式
表达式 描述
nodename 选取此节点的所有子节点.
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性

百搭牌 (Wild-Card) *

百搭牌 (Wild-Card) *

星号 * 可以代替任意一个标签.

例如: /*/*/PRICE 代表所有在第三嵌套层次的 PRICE 对象.

例子: /BARS/*

例子: /BARS/*

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/>
    ...
</BARS>

代表第二嵌套层次中的所有 BAR 子元素和所有 BEER 子元素, 等等.

选择条件

选择条件

如果需要查找某个特定的节点或者包含某个指定值的节点, 则可以在标签名后面跟一个方括号, 里面放置所要满足的条件. 如: /BARS/BAR/PRICE[@theBeer="Miller"]

则只有 theBeer 属性值等于 Miller 的那些 /BARS/BAR/PRICE 元素才包含在路径表达式的执行结果中.

例子: 选择条件

例子: 选择条件

/BARS/BAR/PRICE[.< 2.75], 其中 . 代表当前元素.

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

这里的条件是 PRICE 必须小于 $2.75$.

例子: 选择属性

例子: 选择属性

/BARS/BAR/PRICE[@theBeer="Miller"]

<BARS>
  <BAR name = "JoesBar">
    <PRICE theBeer = "Bud">2.50</PRICE>
    <PRICE theBeer = "Miller">3.00</PRICE>
  </BAR>
    ...
  <BEER name = "Bud" soldBy = "JoesBar
    SuesBar ... "/> ...
</BARS>

注意 PRICE 元素被整个选取了, 包括位于其他 /BARS/BAR 元素中满足条件的 PRICE 子元素.

轴(Axes)

轴(Axes)

轴可定义相对于当前节点的节点集.

一般的, 路径表达式允许我们从根标签开始按照步骤去找出每一步的节点集.

在每一步, 我们可以跟随一些轴(axes)中的某一个.

默认轴是 child::, 即到达当前节点集的所有子节点.

例子: 轴(Axes)

例子: 轴(Axes)

/BARS/BEER 实际上代表 /BARS/child::BEER

@ 实际上代表 attribute:: 轴.

更多轴(More Axes)

更多轴(More Axes)

一些有用的轴是:

  1. parent:: = 当前节点(集)的父节点(集)
  2. descendant-or-self:: = 当前节点(集)以及所有的后代.
    • 注意: // 实际上就是这个轴的简写.
  3. ancestor:: = 先辈
  4. ancestor-or-self = 先辈或自己
  5. self = 自己, 即 .

轴(Axes)
轴名称 结果
ancestor 选取当前节点的所有先辈(父、祖父等)。
ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
attribute 选取当前节点的所有属性。
child 选取当前节点的所有子元素。
descendant 选取当前节点的所有后代元素(子、孙等)。
descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following 选取文档中当前节点的结束标签之后的所有节点。
namespace 选取当前节点的所有命名空间节点。
parent 选取当前节点的父节点。
preceding 选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling 选取当前节点之前的所有同级节点。
self 选取当前节点。

XQuery

XQuery

XQueryXPath 扩充成为一种查询语言, 类似于 SQL

使用相同的项目序列数据模型.

XQuery 是一种表达式语言.

更多关于 Item Sequences

更多关于 Item Sequences

XQuery 有时将会形成序列的序列.

所有序列都是“被压平的”(flattened).

例如: (1 2 () (3 4)) =(1 2 3 4). 其中 () 是空序列(empty sequence).

FLWR 表达式

FLWR 表达式

FLWR (发音为 flower) 表达式, 在某种程度上类似于 SQL 中的 select-from-where 表达式.

  1. 零个或多个 for 子句和 let 子句
  2. 然后是一个可选的 where 子句.
  3. 最后是一个 return 语句.

FLWR 表达式的语义

FLWR 表达式的语义

每个 for 创建了一个循环.

对于每一层嵌套的循环,

如果 where 子句返回 TRUE, 则执行 return 语句, 并且将返回的值追加到输出中.

FOR 语句

FOR 语句

for <variable> in <expression>, ...

例子: FOR

例子: FOR

for $beer in
  document("bars.xml")/BARS/BEER/@name
return
  <BEERNAME> {$beer} </BEERNAME>

花括号的使用

花括号的使用

假定有一个变量 $x, 它的值是 abc.

LET 子句

LET 子句

let <variable> := <expression>, ...

例子: LET 子句

例子: LET 子句

let $d := document("bars.xml")
let $beers := $d/BARS/BEER/@name
return
  <BEERNAMES> {$beers} </BEERNAMES>

返回所有啤酒的名字这样的元素序列. 如:

Order-By 子句

Order-By 子句

FLWR 实际上是 FLWOR: 在 return 语句之前可以有 order-by 子句.

形式为: order by <expression>

其中的表达式 <expression> 对每个变量的赋值都

例子: Order-By

例子: Order-By

列出 Bud 啤酒的所有售价, 并按从低到高排序.

let $d := document("bars.xml")
for $p in
  $d/BARS/BAR/PRICE[@theBeer="Bud"]
order by $p
return $p

SQL ORDER BY

SQL ORDER BY

SQL 也是同样工作的, 只是对于 FROM ... WHERE ... 所得的结果关系排序, 而不是对输出排序.

例如: 对于关系 R(a,b),

SELECT b FROM R
WHERE b > 10
ORDER BY a;

谓语(Predicates)

谓语(Predicates)

以量化形式的条件.

例子: /BARS/BAR[@name] 意思是所有含有属性 name 的酒吧.

例子: /BARS/BEER[@soldAt="Joe's Bar"] 得到的结果是所有在 Joe's Bar 出售的啤酒.

例子: 比较

例子: 比较

我们来列出 Joe 酒吧出售的所有啤酒的 PRICE 元素,

输出要求是一个 BBP 元素. 它有两个属性, 其中一个属性是 bar, 值为酒吧名; 另一个属性是 beer, 值是啤酒名, 而价格作为BBP元素的值.

策略(Strategy)

策略(Strategy)

  1. 建立一个三重 for 循环, 变量遍历所有的 BEER 元素, 以及那些 BAR 元素中的所有 PRICE 元素.
  2. 检查此啤酒是否由 Joe's Bar 所销售, 并且啤酒的名称是否和 PRICE 元素中的 theBeer 属性的值一致.
  3. 构造输出元素.

The Query

The Query

let $bars = doc("bars.xml")/BARS
for $beer in $bars/BEER
  for $bar in $bars/BAR
    for $price in $bar/PRICE
      where $beer/@soldAt = "Joe's Bar" and
      $price/@theBeer = $beer/@name
      return <BBP bar = {$bar/@name} beer
        = {$beer/@name}>{$price}</BBP>

where 中条件 $beer/@soldAt = "Joe's Bar"True, 当且仅当 "Joe's Bar" 出现在 XPath 表达式 $beer/@soldAt 的结果序列的任一位置.

严格比较(Strict Comparisons)

严格比较(Strict Comparisons)

如果要求比较的对象是单元素构成的序列, 则使用 Fortran 比较算子:

例如: $beer/@soldAt eq "Joe's Bar" 为真当且仅当 Joe's Bar 是唯一销售此啤酒的酒吧.

元素与值的比较

元素与值的比较

当一个元素与一个基本值作比较时, 若元素的值是原子性的(atomic), 则元素作为值来处理.

例如:
/BARS/BAR[@name="Joe's Bar"]/PRICE[@theBeer="Bud"] eq "2.50"
为真当且仅当 Joe's Bar 以 $\$2.50$ 的价格售卖 Bud.

两个元素的比较

两个元素的比较

两个元素即使看起来很像, 有时也不一定相等. 如:

/BARS/BAR[@name="JoesBar"]/
PRICE[@theBeer="Bud"] eq
/BARS/BAR[@name="SuesBar"]/
PRICE[@theBeer="Bud"]

返回 false. 即使 JoeSue 关于 Bud 啤酒卖同样的价钱.

对于相等的元素, 它们必须是完全相同的. (physically in the implied document)

敏锐(Subtlety): 元素实际上是指向特殊文档的指针, 而不是出现在节中的下一个字符串.

从元素中取得数据

从元素中取得数据

假设我们要比较元素的值, 而不是比较它们在文档中的位置.

如果要从元素 E 只提取数据(例如得到价格本身). 则使用 data(E).

例子: data()

例子: data()

return <BBP bar = {$bar/@name}
  beer = {$beer/@name} price =
  {data($price)} />

消除重复(Eliminating Duplicates)

消除重复(Eliminating Duplicates)

将函数 distinct-values 应用到某个序列上.

Subtlety: this function strips tags away from elements and compares the string values.

例子: 所有不同的价格

例子: 所有不同的价格

return distinct-values(
  let $bars = doc("bars.xml")
  return $bars/BARS/BAR/PRICE
)

记住: XQuery 是一种表达式语言. 一个 XQuery 系列可以出现在任意一个值可以出现的地方.

有效的布尔值(Effective Boolean Values)

有效的布尔值(Effective Boolean Values)

表达式的有效布尔值(effective boolean values, EBV)是指

  1. 如果表达式是布尔类型的, 则表达式取实际的值.
  2. 如果表达式为 0 或者是空字符串 "", 或是空序列(), 则返回 FALSE.
  3. 其他的都认为是 TRUE.

EBV 例子

EBV 例子

  1. @name="Joe's Bar" 的 EBV 是 TRUEFALSE, 这取决于 name 属性是否为 Joe's Bar.
  2. /BARS/BAR[@name="Golden Rail"]
    当某个酒吧的名称为 Golden Rail 时, 方括号中表达式的 EBV 为 TRUE; 否则为 FALSE.

布尔算子(Boolean Operators)

布尔算子(Boolean Operators)

$E_1$ and $E_2$, $E_1$ or $E_2$, not($E$) 可应用于任何表达式.

首先对于表达式求它们的 EBV.

例如: not(3 eq 5 or 0) 的 EBV 是 TRUE.

true()false() 是两个函数, 分别返回 TRUEFALSE.

分支表达式(Branching Expressions)

分支表达式(Branching Expressions)

表达式 if($E_1$) then $E_2$ else $E_3$ 是这样计算的:

例子: 如果酒吧是 Joe's Bar 则返回 $bar 的子元素 PRICE.

if($bar/@name eq "Joe's Bar")
  then $bar/PRICE else ()

() 是指空序列. 注意这里没有 if-then 表达式.

限定符表达式(Quantifier Expressions)

限定符表达式(Quantifier Expressions)

some $\$x$ in $E_1$ satisfies $E_2$

  1. 计算序列 $E_1$;
  2. $x 遍历序列 $E_1$ 中的每一项, 然后计算 $E_2$;
  3. 如果 $E_2$ 对于至少一个 $x 的 EBV 为 TRUE, 则返回 TRUE.

类似的的表达式
every $\$x$ in $E_1$ satisfies $E_2$

例子: Some

例子: Some

求至少销售一种低于 $\$2$ 的啤酒的酒吧.

for $bar in
  doc("bars.xml")/BARS/BAR
where some $p in $bar/PRICE
  satisfies $p < 2.00
return $bar/@name

注意: 使用 where $bar/PRICE < 2.00 也可以的.

例子: Every

例子: Every

列出没有 $\$5$ 以上啤酒的酒吧.

for $bar in
  doc("bars.xml")/BARS/BAR
where every $p in $bar/PRICE
  satisfies $p <= 5.00
return $bar/@name

文档次序(Document Order)

文档次序(Document Order)

文档次序的比较使用 <<>>

例子:

$d/BARS/BEER[@name="Bud"]
<< $d/BARS/BEER[@name="Miller"]

这个表达式为真当且仅当在文档 $d 中, Bud 元素在 Miller 元素之前出现.

集合算子(Set Operators)

集合算子(Set Operators)

union, intersect, except 可以作用在节点序列上.

Java 与 XQuery

Java 与 XQuery

JDK 内置了XPath的一个实现, 可以通过 javax.xml.path 这个API来使用. 这个API 也可以被其他的XPath实现使用, 比如 Saxon 和 Jaxen.

没有与 JDK 一起发行的 XQuery 引擎, 但是有一些第三方的产品. 可以访问 W3C XQuery 页面, 那里有一些. 其中一个简单的就是 Saxon.

从 Java 访问 XQuery 有一个标准的API, 称为 XQJ(打包成 javax.xml.xquery),

Saxon 有另一个界面, 称为 s9api.

References

https://stackoverflow.com/questions/13403170/how-can-i-run-an-xquery-on-an-xml-file-using-javax-xml-xpath

http://saxon.sourceforge.net/ https://sourceforge.net/projects/saxon/files/

XSLT

XSLT

XSLT扩展的样式语言(extensible stylesheet language - transforms) 是处理 XML 文档的一种说明性语言. 用于将XML文档(包括XHTML文档)转换为其他XML、XHTML、HTML或文本文件.

最初是作为一种呈现语言的: 将 XML 文档(不能被显示)转换为 HTML 页面.

也可以执行 XMLXML 的转换, 因此可以作为一种查询语言.

实验

上机实践: 点击 test_bar.xml, 你将会看到此 XML 文档在 test_bar.xsl 下的输出效果.

或者使用软件如 Xalan 将 test_bar.xml 根据 test.bar.xsl 中的规则转换为 test_bar.html.

Xalan -o test_bar.html test_bar.xml test_bar.xsl

Windows 系统下可以安装 Microsoft 的免费 Win32 可执行 XSLT 处理器 MSXSL.
下载地址: http://www.microsoft.com/en-us/download/details.aspx?id=21714, 查找 MSXSL.

XSLT 程序

XSLT 程序

XML Schema 类似, XSLT 本身也是一个 XML 文档.

XSLT 有自己的标签命名空间, 通常被记为 xsl:

模板(Templates)

模板(Templates)

xsl:template 元素描述了(将要被处理的文档的)一族元素, 以及对它们将要作何处理.

形式如下:

<xsl:template match = path >
  ...
</xsl:template>

match 属性给出一个 XPath 表达式, 描述如何找到模板要应用的节点(集).

例子: BARS Document --> Table

例子: BARS Document --> Table

我们将把 bars.xml 转换为 HTML 文档, 就像描述关系 Sells(bar,beer,price) 时所用的表格一样.

第一个模板将匹配文档的根节点 /, 并生成一个空表.

根节点的模板

根节点的模板

<xsl:template match = "/">
  <table>
    <tr>
      <th>bar</th>
      <th>beer</th>
      <th>price</th>
    </tr>
  </table>
</xsl:template>

match="/" 表示该模板仅匹配根节点.

蓝色部分的 html 代码就是模板的输出, 这是一张表格, 只有表格头, 没有数据行.

显然, 这样是没有办法插入数据行的. 需要改进.

策略概要(Outline of Strategy)

策略概要(Outline of Strategy)

  1. 对每个酒吧元素 BAR, 利用 xsl:variable 变量 b 存储该酒吧的名称.
  2. 利用 xsl:for-each, 对每个 PRICE 元素, 生成啤酒的名称和价格. 这里啤酒的名称和价格要利用刚才的变量 bxsl:value-of.

模板的递归使用(Recursive use of templates)

模板的递归使用(Recursive use of templates)

一个 XSLT 文档通常包含很多模板.

从应用于根节点的第一个模板开始.

任意模板中可以包含标签 <xsl:apply-templates/>, 它将从当前节点开始, 对它子元素递归应用此模板.

应用模板(Apply-Templates)

应用模板(Apply-Templates)

属性 select 给出了一个 XPath 表达式, 描述哪些子元素将被模板作用.

例如:
<xsl:apply-templates select = "BARS/BAR" />
从当前节点(即模板正在被应用的元素)开始, 对于匹配为 BARS/BAR 的子元素, 将模板应用到它身上.

例子: 应用模板(Apply-Templates)

例子: 应用模板(Apply-Templates)

<xsl:template match = "/">
  <table>
    <tr>
      <th>bar</th>
      <th>beer</th>
      <th>price</th>
    </tr>
  <xsl:apply-templates select = "BARS" />
  </table>
</xsl:template>

提取值(Extracting Values)

提取值(Extracting Values)

<xsl:value-of select = XPath expression />
将生成一个值(字符串)作为输出.

例子: 假定我们正在应用某个模板到 BAR 元素上, 希望将它的名称(属性 name 的值)输出到表格中.

<xsl:value-of select = "@name" />

变量(Variables)

变量(Variables)

我们可以用下们的方式声明一个变量:
<xsl:variable name = "x" />

例如:

<xsl:variable name = "bar">
  <xsl:value-of select = "@name" />
</xsl:variable>

将上面的代码放在应用于 BAR 元素的模板中, 则将该酒吧的名称赋给变量 bar.

变量的使用

变量的使用

当使用刚才定义的变量时, 只需在它前面加上符号 $.

例如: <td>$bar</td>

完成表格(Completing the Table)

完成表格(Completing the Table)

  1. 我们将对每个 BAR 元素应用一个模板.
  2. 这个模板将酒吧的名称赋给变量 b, 并且对每个 PRICE 子元素进行迭代.
  3. 对每个 PRICE 子元素, 我们利用 btheBeer 属性及 PRICE 本身打印出该行数据.

循环(Iteration)

循环(Iteration)

<xsl:for-each select = "Xpath expression">
  ...
</xsl:for-each>

对当前节点(由XPath表达式指定)的每个子节点执行 for-each 迭代.

BARS 的模板

BARS 的模板

<xsl:template match = "BAR">
  <xsl:variable name = "b">
    <xsl:value-of select = "@name" />
  </xsl:variable>
  <xsl:for-each select = "PRICE">
    <tr>
      <td>$b</td>
      <td><xsl:value-of select = "@theBeer" /></td>
      <td><xsl:value-of select = "data(.)" /></td>
    </tr>
  </xsl:for-each>
</xsl:template>

End






Thanks very much!

This slide is based on Jeffrey D. Ullman's work, which can be download from his website.