知优码

您现在的位置是:首页 > WEB相关 > XML文档

XML文档

XML-XPath语言

知优码 2019-11-17XML文档
在上一篇笔记的结尾,我们接触到了两个用于选择XML文档中特定范围的元素,这两个元素的取值都是XPath表达式,那么,什么是XPath呢?简单的说,XPath是用于在

  在上一篇笔记的结尾,我们接触到了两个用于选择XML文档中特定范围的元素<selector>和<field>,这两个元素的取值都是XPath表达式,那么,什么是XPath呢?简单的说,XPath是用于在XML文档中查找信息的语言,可用来在XML文档中遍历元素和属性,很多XML的相关技术比如XSLT、XQuery、XPointer等都是构建于XPath的基础之上,在这一篇笔记中,就来学习一下XPath语言。

1、相关术语

(1)节点(Node):格式良好的XML文档都可以转换为一个树型结构,XPath中的节点也就是这个树型结构中的节点。概况起来,有如下所列的七种节点:

节点类型 说明
XML文档根节点 XML文档的根称为文档节点或根节点
元素节点 一个元素的开始标签、结束标签,以及之间的全部内容整体称之为元素节点
属性节点 元素的每个属性都构成一个属性节点,包括属性名称和属性值两个部分,属性节点必须依附于元素节点
命名空间节点 XML文档中的xmlns:prefix属性称之为命名空间节点,注意和属性节点的区别
文本节点 XML元素中间的字符数据,包括CDATA段中的字符数据
注释节点 XML文档里<!--和-->包含的注释部分构成注释节点
处理指令节点 XML文档的处理指令部分构成处理指令节点

(2)基本值(也称为原子值,Atomic Value):专门用于表示简单的字面值,如整数值,字符串等。基本值可以当成没有父节点也没有子节点的节点。

(3)项目(Item):一个项目代表一个基本值或一个节点。

(4)节点集和序列(Sequence):XPath表达式可以表示多个节点,多个节点的组合在XPath1.0中称为节点集,而在XPath2.0中添加了一个序列的术语,即可以代表普通的项目,也可以代表节点集。

(5)节点关系:

节点关系 说明
父节点Parent 每个元素或属性都有一个父节点
子节点Children 元素节点可以有0个、1个或多个子节点
兄弟节点Sibling 父节点相同的节点称之为兄弟节点
祖先节点Ancestor 节点的父节点、父节点的父节点一直到根节点
后代节点Descendant 节点的子节点,子节点的子节点......

(6)相对路径和绝对路径:与操作系统中的路径类似,XPath中也有相对路径和绝对路径,绝对路径以斜线(/)开头,总是从根节点开始匹配,而相对路径则不会以斜线开头,从当前路径开始匹配。

2、XPath语法

  XPath使用路径表达式来访问XML中的节点或节点集,每个XPath表达式总是由一个或多个步(step)组成的,多个步直接使用斜线分隔。在XPath中,步的语法格式如下:

::节点测试[限定谓语]

也就是说,每个步都通过了3次筛选,第一次是使用“”选择节点方向,第二次使用“节点测试”选取在指定轴方向上的部分节点,第三次则是使用“限定谓语”来对选中的节点进一步过滤。

(1)轴:在XPath中,有下面列表中的各种轴:

简化写法 说明
ancestor   选取当前节点的所有先辈(父、祖父等)节点
ancestor-or-self   选取当前节点的所有先辈(父、祖父等)节点以及当前节点本身
attribute 选取当前节点的所有属性节点,如果当前节点不是元素节点,则attribute轴方向上的节点集为空
child 省略不写  选取当前节点的所有子节点
descendant //  选取当前节点的所有后代节点(子、孙等)
descendant-or-self   选取当前节点的所有后代节点(子、孙等)以及当前节点本身
following   选取文档中当前节点的结束标签之后的所有节点,不会包含当前节点的后代节点和属性节点
following-sibling   选取文档中当前节点的结束标签之后的所有兄弟节点
namespace   选取当前节点的所有命名空间节点,当前节点不是元素节点,则namespace轴方向上的节点集为空
parent .. 选取当前节点的父节点
preceding   选取文档中当前节点的开始标签之前的所有节点,不会包含当前节点的后代节点和属性节点
preceding-sibling   选取文档中当前节点的开始标签之前的所有兄弟节点
self . 选取当前节点

(2)节点测试:节点测试用于从指定轴方向上选取所匹配的特定的节点,在XPath中,常用的节点测试语法如下表所示:

节点测试 说明
nodename

从指定轴方向上选出具有nodename的节点

如child::book选择当前节点的所有book子节点

   descendant::book选择当前节点的所有book后代节点(包括book子节点、孙子节点等)

node() 选择指定轴匹配的所有类型的节点
text()

选择指定轴匹配的所有文本类型的节点

如child::text()选择当前节点的所有文本子节点

  descendant::text()选择当前节点的所有文本后代节点(包括文本子节点、文本孙子节点等)

comment() 选择指定轴匹配的所有注释节点
processing-instruction 选择指定轴匹配的所有处理指令节点
* 节点测试中的通配符,表示所有,也即不进行过滤

(3)限定谓语:每个步中可以接受0个或多个限定谓语,用于进一步过滤所选取的节点集,限定谓语放在方括号中,通常限定谓语返回一个boolean值。看下面的一些例子:

路径表达式 结果
/bookstore/book[1] 选取属于 bookstore  元素的子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore  元素的子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore  元素的子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

3、运算符

  从上面的实例中可以看到,在限定谓语中,还可以使用运算符、表达式,还有很多内置的函数供使用。这一小节先看看XPath中支持的运算符:

(1)算术运算符:加(+)、减(-)、乘(*)、除(div)、取模(mod)

  算术运算符非常简单,但是需要注意几点:

A、因为减号实际上也就是中划线,而中划线在XML中是合法的标识符号,从而带来了歧义,于是XPath强制规定,使用减号的时候,需要前后各加一个空格。

B、在XPath中,所有的数值都是64位的double类型,即便直接书写成0、100;另外,XPath还有几个特殊的数值:正无穷大、负无穷大、非数。

C、在运算时,如果操作数不是数值类型,会自动转换,下面的比较运算符、逻辑运算符如果有必要也会发生相应的自动类型转换。

(2)比较运算符:等于(=)、不等于(!=)、小于(<)、小于或等于(<=)、大于(>)、大于或等于(>=)

  需要注意的是,不像其它编程语言,这里表示相等只需要一个等于号。

(3)逻辑运算符:与(and)、或(or)

(4)集合运算符:并集(|)

4、表达式

(1)for表达式:用于循环访问序列中的每个项,并对每项进行一次计算,最后将每项计算得到的结果组合成序列后返回,语法格式如下:

for $var in sequence return rtExpression

实际上,这里的for更类似于js中的foreach。还可以使用下面的形式遍历多个序列:

for $var1 in sequence1, $var2 in sequence2 return fn($var1,$var2)

(2)if表达式:用于处理分支,根据不同条件得到不同的返回值,语法格式如下:

if (condition1)
then rtVal1
[else if (condition2)
then rtVal2
...]
else
otherVal

(3)some表达式:迭代中只要有一项满足条件就返回true,否则返回false,语法格式如下:

some $var in sequence satisfies condition

(4)every表达式:迭代中只有有一项不满足条件就返回false,否则返回true,语法格式如下:

every $var in sequence satisfies condition

5、内置函数库

  在XPath中还有大量的内置函数,用于增强相关功能,这些内置函数可以参考:XPath函数。我在下面也抄录一份供参考:

分类 函数 说明
存取函数 fn:node-name(node) 返回参数节点的节点名称。
fn:nilled(node) 返回是否拒绝参数节点的布尔值。
fn:data(item.item,...) 接受项目序列,并返回原子值序列。
  • fn:base-uri()
  • fn:base-uri(node)
返回当前节点或指定节点的 base-uri 属性的值。
  • fn:document-uri()
  • fn:document-uri(node)
返回当前节点或指定节点的 document-uri 属性的值。
错误和跟踪函数
  • fn:error()
  • fn:error(error)
  • fn:error(error,description)
  • fn:error(error,description,error-object)

例子:error(fn:QName('http://example.com/test', 'err:toohigh'), 'Error: Price is too high')

结果:向外部处理环境返回 http://example.com/test#toohigh 以及字符串 "Error: Price is too high"。

fn:trace(value,label) 用于对查询进行 debug。
数值函数 fn:number(arg)

返回参数的数值。参数可以是布尔值、字符串或节点集。

例子:number('100')

结果:100

fn:abs(num) 返回参数的绝对值。
fn:ceiling(num) 返回大于或等于 num 参数的最小整数。
fn:floor(num) 返回小于或等于 num 参数的最大整数。
fn:round(num) 把 num 参数四舍五入为最接近的整数。
fn:round-half-to-even()

返回最接近参数num的偶数

例子:round-half-to-even(0.5) 结果:0

例子:round-half-to-even(1.5) 结果:2

例子:round-half-to-even(2.5) 结果:2

字符串函数 fn:string(arg) 返回参数的字符串值。参数可以是数字、逻辑值或节点集。 
fn:codepoints-to-string(int,int,...)

根据一个Unicode值序列序列返回字符串。

例子:codepoints-to-string((84, 104, 233, 114, 232, 115, 101))

结果:'Thérèse'

注意:该函数的参数是一个Unicode值序列,因此必须用括号将参数括起来

fn:string-to-codepoints(string) 根据字符串返回每个字符所对应的Unicode值的序列。
fn:codepoint-equal(comp1,comp2) 根据 Unicode 值序列比较,如果 comp1 的值等于 comp2 的值,则返回 true,否则返回 false。 
  • fn:compare(comp1,comp2)
  • fn:compare(comp1,comp2,collation)

根据对照规则,comp1小于comp2返回 -1;comp1等于comp2,返回0;comp1大于comp2返回1。

fn:concat(string,string,...) 返回字符串的拼接。
fn:string-join((string,string,...),sep) 使用 sep 参数作为分隔符,来返回 string 参数拼接后的字符串。
  • fn:substring(string,start,len)
  • fn:substring(string,start)

返回从 start 位置开始的指定长度的子字符串。第一个字符的下标是 1。

如果省略 len 参数,则返回从位置 start 到字符串末尾的子字符串。

  • fn:string-length(string)
  • fn:string-length()
返回指定字符串的长度。如果没有 string 参数,则返回当前节点的字符串值的长度
  • fn:normalize-space(string)
  • fn:normalize-space()
删除指定字符串的首尾空白,并把内部连续空白压缩为一个,然后返回结果。没有参数,则处理当前节点。 
fn:normalize-unicode() 执行 Unicode 规格化。
fn:upper-case(string)  把 string 参数转换为大写。
fn:lower-case(string)  把 string 参数转换为小写。
fn:translate(string1,string2,string3) 

把 string1 中的 string2 替换为 string3。

例子:translate('12:30','30','45')

结果:'12:45'

例子:translate('12:30','03','54')

结果:'12:45'

例子:translate('12:30','0123','abcd')

结果:'bc:da'

fn:escape-uri(stringURI,esc-res) 

例子:escape-uri("http://example.com/test#car", true())

结果:"http%3A%2F%2Fexample.com%2Ftest#car"

例子:escape-uri("http://example.com/test#car", false())

结果:http://example.com/test#car

例子:escape-uri ("http://example.com/~bébé", false())

结果:"http://example.com/~b%C3%A9b%C3%A9"

fn:contains(string1,string2)  如果 string1 包含 string2,则返回 true,否则返回 false。
fn:starts-with(string1,string2)  如果 string1 以 string2 开始,则返回 true,否则返回 false。
fn:ends-with(string1,string2)  如果 string1 以 string2 结尾,则返回 true,否则返回 false。
fn:substring-before(string1,string2)  返回 string2 在 string1 中出现之前的子字符串。
fn:substring-after(string1,string2)  返回 string2 在 string1 中出现之后的子字符串。
fn:matches(string,pattern)  如果 string 参数匹配指定的模式,则返回 true,否则返回 false。
fn:replace(string,pattern,replace)  把指定的模式替换为 replace 参数,并返回结果。
fn:tokenize(string,pattern)

例子:tokenize("XPath is fun", "\s+")

结果:("XPath", "is", "fun")

anyURI函数 fn:resolve-uri(relative,base)  
逻辑函数 fn:boolean(arg)  返回数字、字符串或节点集的布尔值。
fn:not(arg)  首先通过 boolean() 函数把参数还原为一个布尔值,然后再取反。 
fn:true()  返回布尔值 true。 
fn:false()  返回布尔值 false。 
日期时间函数 fn:dateTime(date,time)  把参数转换为日期和时间。 
fn:years-from-duration(datetimedur) 返回参数值的年份部分的整数,以标准词汇表示法来表示。 
fn:months-from-duration(datetimedur)  返回参数值的月份部分的整数,以标准词汇表示法来表示。 
fn:days-from-duration(datetimedur)  返回参数值的天部分的整数,以标准词汇表示法来表示。 
fn:hours-from-duration(datetimedur)  返回参数值的小时部分的整数,以标准词汇表示法来表示。 
fn:minutes-from-duration(datetimedur)  返回参数值的分钟部分的整数,以标准词汇表示法来表示。 
fn:seconds-from-duration(datetimedur)  返回参数值的分钟部分的十进制数,以标准词汇表示法来表示。 
fn:year-from-dateTime(datetime)  返回参数本地值的年部分的整数。 
fn:month-from-dateTime(datetime)  返回参数本地值的月部分的整数。 
fn:day-from-dateTime(datetime)  返回参数本地值的天部分的整数。 
fn:hours-from-dateTime(datetime)  返回参数本地值的小时部分的整数。 
fn:minutes-from-dateTime(datetime)  返回参数本地值的分钟部分的整数。 
fn:seconds-from-dateTime(datetime)  返回参数本地值的秒部分的十进制数。 
fn:timezone-from-dateTime(datetime)  返回参数的时区部分,如果存在。 
fn:year-from-date(date)  返回参数本地值中表示年的整数。 
fn:month-from-date(date)  返回参数本地值中表示月的整数。 
fn:day-from-date(date) 返回参数本地值中表示天的整数。 
fn:timezone-from-date(date)  返回参数的时区部分,如果存在。 
fn:hours-from-time(time)  返回参数本地值中表示小时部分的整数。 
fn:minutes-from-time(time) 返回参数本地值中表示分钟部分的整数。 
fn:seconds-from-time(time)  返回参数本地值中表示秒部分的整数。 
fn:timezone-from-time(time)  返回参数的时区部分,如果存在。 
fn:adjust-dateTime-to-timezone(datetime,timezone)  如果 timezone 参数为空,则返回没有时区的 dateTime。否则返回带有时区的 dateTime。 
fn:adjust-date-to-timezone(date,timezone) 如果 timezone 参数为空,则返回没有时区的 date。否则返回带有时区的 date。 
fn:adjust-time-to-timezone(time,timezone) 如果 timezone 参数为空,则返回没有时区的 time。否则返回带有时区的 time。 
 QName相关函数  fn:QName()   
fn:local-name-from-QName()  
fn:namespace-uri-from-QName()   
fn:namespace-uri-for-prefix()   
fn:in-scope-prefixes()   
fn:resolve-QName()   
节点函数 
  •  
  • fn:name()
  • fn:name(nodeset)
返回当前节点的名称或指定节点集中的第一个节点。 
  • fn:local-name()
  • fn:local-name(nodeset)
返回当前节点的名称或指定节点集中的第一个节点 - 不带有命名空间前缀。 
  • fn:namespace-uri()
  • fn:namespace-uri(nodeset)
返回当前节点或指定节点集中第一个节点的命名空间 URI。 
fn:lang(lang) 

如果当前节点的语言匹配指定的语言,则返回 true。

例子:Lang("en") is true for <p xml:lang="en">...</p>

例子:Lang("de") is false for <p xml:lang="en">...</p>

  • fn:root()
  • fn:root(node)
返回当前节点或指定的节点所属的节点树的根节点。通常是文档节点。 
上下文函数 fn:position() 

返回当前正在被处理的节点的 index 位置。

例子://book[position()<=3]

结果:选择前三个 book 元素

fn:last() 

返回在被处理的节点列表中的项目数目。

例子://book[last()]

结果:选择最后一个 book 元素 

fn:current-dateTime()  返回当前的 dateTime(带有时区)。 
fn:current-date()  返回当前的日期(带有时区)。 
fn:current-time()  返回当前的时间(带有时区)。 
fn:implicit-timezone()  返回隐式时区的值。 
fn:default-collation()  返回默认对照的值。
fn:static-base-uri()  返回 base-uri 的值。 
序列函数 一般序列函数 fn:index-of((item,item,...),searchitem) 

返回在项目序列中等于 searchitem 参数的位置。

例子:index-of ((15, 40, 25, 40, 10), 40)

结果:(2, 4)

fn:remove((item,item,...),position)  返回由 item 参数构造的新序列 - 同时删除 position 参数指定的项目。 
fn:empty(item,item,...)  如果参数值是空序列,则返回 true,否则返回 false。 
fn:exists(item,item,...)  如果参数值不是空序列,则返回 true,否则返回 false。 
fn:distinct-values((item,item,...),collation) 

返回唯一不同的值。

例子:distinct-values((1, 2, 3, 1, 2))

结果:(1, 2, 3) 

fn:insert-before((item,item,...),pos,inserts)  返回由 item 参数构造的新序列 - 同时在 pos 参数指定位置插入 inserts 参数的值。 
fn:reverse((item,item,...))  返回指定的项目的颠倒顺序。 
fn:subsequence((item,item,...),start,len)  返回 start 参数指定的位置返回项目序列,序列的长度由 len 参数指定。第一个项目的位置是 1。 
fn:unordered((item,item,...)) 依据实现决定的顺序来返回项目。 
容量测试函数 fn:zero-or-one(item,item,...)  如果参数包含零个或一个项目,则返回参数,否则生成错误。 
fn:one-or-more(item,item,...)  如果参数包含一个或多个项目,则返回参数,否则生成错误。 
fn:exactly-one(item,item,...)  如果参数包含一个项目,则返回参数,否则生成错误。 
比较函数 fn:deep-equal(param1,param2,collation) 如果 param1 和 param2 与彼此相等(deep-equal),则返回 true,否则返回 false。
合计函数 fn:count((item,item,...))  返回节点的数量。 
fn:avg((arg,arg,...)) 返回参数值的平均数。
fn:max((arg,arg,...)) 返回参数中的最大值。
fn:min((arg,arg,...)) 返回参数中的最小值。
fn:sum(arg,arg,...) 返回指定节点集中每个节点的数值的总和。
序列生成函数 fn:id((string,string,...),node)  
fn:idref((string,string,...),node)  
fn:data((item1,item2,...)) 返回item1、item2等各项的值所组成的序列。
fn:doc(URI)  
fn:doc-available(URI) 如果 doc() 函数返回文档节点,则返回 true,否则返回 false。
  • fn:collection()
  • fn:collection(string)
 

很明显,把这些内置函数放在这里的目的并不是要强行记住,而只是需要的时候当字典查查(XQuery1.0和XPath共享了这些内置函数,有事没事看看,混个眼熟也挺好的)。