xquery
尽管XPath和XQuery都执行某些相同的功能,但是XPath提供了简单性,而XQuery提供了更多的功能和灵活性。 XPath是许多类型查询的理想工具。 例如,XPath是您从XML文档中的记录子集创建电话号码无序列表的最简单方法。 但是,如果您需要一个表达更复杂的记录选择条件,转换结果集或需要递归的查询,则需要XQuery。
XPath是一种特定于领域的语言(DSL),正在Swift成为其他更多通用语言的重要组成部分。 编程语言通过模块和类将XPath合并,并且在某些情况下直接将其合并到语言的语法中。 这类似于前一段时间正则表达式发生的情况。
XPath之所以受欢迎,是因为从XML文档中提取特定的数据位时,该语言可以节省开发人员大量的时间和精力。 即使以前从未使用过XPath的个人也可以快速利用其功能。 考虑清单1中的XML片段。
<users>
<user>
<name>
<first>Lola</first>
<last>Solis</last>
</name>
<age>2</age>
</user>
<user>
<name>
<first>Nina</first>
<last>Serafina</last>
</name>
<age>4</age>
<visits>
<first>2008-01-15</first>
<last>2008-02-15</last>
</visits>
</user>
<user>
<name>
<first>Tracy</first>
<last>Keller</last>
</name>
<age>35</age>
</user>
</users>
如果要获取此文档中子项的姓氏列表,则可以使用以下XPath表达式。
/user[age lt 18]/name/last/text()
(: Result
Solis
Serafina
:)
想象一下在没有XPath的情况下提取数据所必须编写的代码。 即使借助正则表达式,您也需要考虑一下如何从visits节点中的最后一个标记中排除该值。
上面的XPath表达式不仅简洁,而且非常清楚。 快速浏览即可发现该表达式的功能,即使对于不了解XPath的人也是如此。 XPath之所以起作用,是因为它功能强大,并且拥有悠久的历史。 该语言理解具有任意复杂度的XML文档中的节点,更重要的是,可以理解那些节点之间的关系。 因此,您可以编写简洁的表达式,不仅考虑元素和元素值,还考虑属性,处理指令等。
与XPath相比,许多XML查询很难以更简洁明了的方式表达。 但是XPath的DSL性质和该语言的目标对程序员施加了一些相当严重的限制。 以下各节简要介绍了XQuery语言,并显示了XPath本身无法解决的问题。 这些问题要求程序员从XPath转向XQuery之类的工具。
由于XQuery本身支持XPath,因此作为XQuery语法的一部分,XQuery显然可以完成XPath可以完成的所有工作。 但是XQuery是图灵完备的,可以被认为是通用语言。 它以克服一些复杂性为代价,轻松克服了XPath的许多限制。
XQuery使用一种简单的语法,该语法混合了XML,XPath,注释,函数和特殊的表达式语法,以将它们结合在一起。 XQuery代码完全由不带语句的表达式组成。 所有的值都是序列,简单对语言很重要。 因此,表达式Hi
和2 * 2
都是有效的XQuery代码,无需任何前奏或修改即可执行。 XQuery是一种高级,强类型的功能语言(没有副作用),非常适合表示查询以从XML文档和大型XML文档存储库中获取数据。 在最后一个方面,它很像SQL。 但是XQuery还提供了表示结果集的任意转换的功能。 就像当您想从XML文档中检索某些数据时使用XPath会很有意义,而当您想从XML文档的大型存储库中检索和转换数据时,使用XQuery也会很有意义。
XPath的一个明显限制是它不提供以任何方式转换结果集的方式。 假设您想按字母顺序返回先前的XPath查询( 清单2)的结果,如清单3所示。
Serafina
Solis
您不能使用XPath做到这一点。 为此,您必须用另一种语言(例如XQuery)编写代码,或使用一些特殊的专有XPath扩展对结果进行排序。
另一方面,XQuery允许您对结果进行排序或将其转换为HTML,CSV,SQL或任何其他基于文本的格式。 您可以使用XQuery进行的最强大的转换类型包括XML到XML的转换。 通常,大型XML数据库可以包含客户端应用程序不需要的各种复杂的,相互关联的XML文档。 XQuery允许客户端精确描述希望服务器返回的XML文档的类型。 通过提供XQuery接口,服务器通常可以避免将数据保留在多个架构中。 而且,使用XQuery转换客户端数据通常比尝试使用Perl或Java或其他流行的计算机语言转换数据容易得多,而且速度更快。 当然,在当前实现中,当您检索数据时使用XQuery转换数据比稍后使用XSLT进行转换要快得多。
为了将记录选择标准和结果转换指令联系在一起,XQuery提供了一种称为FLWOR(发音为“ flower”)表达的功能。 的缩写字母代表for
, let
, where
, order by
和return
。 这些是可以构成FLWOR表达式的元素。 FLWOR表达式至少按首字母缩略词建议的顺序包含其中的某些元素。 所有FLWOR表达式均以for或let表达式开头,以return表达式结尾。 如果您熟悉SQL,您可能已经知道我的工作方向了。 这是一个简单的FLWOR表达式,它借鉴了Edwin Markham的诗“ Outwitted”(参见清单4)。
let $xml:=
<a>
<one>She drew a circle that shut me out</one>
<two>Heretic rebel, a thing to flout</two>
</a>
return $xml//one/text()
(: Result
"She drew a circle that shut me out"
:)
清单5显示了如何将简单的FLWOR表达式应用于清单1中的XML。 (为简便起见,清单中显示了_XML from Listing 1_
中的文本_XML from Listing 1_
,代替了其中的实际XML。)
let $xml:= _XML from Listing 1_
for $user in $xml//user[age lt 18]
order by $user/name/last
return $user/name/last/text()
(: Result
Serafina
Solis
:)
如果希望查询返回一个以数字列表表示结果HTML片段,则可以应用清单6中的XQuery。
let $xml:= _XML from Listing 1_
return
<ol>{
for $user in $xml//user[age lt 18]
order by $user/name/last
return <li>{$user/name/last/text()}</li>
}</ol>
(: Result
<ol><li>Serafina</li><li>Solis</li></ol>
:)
注意清单6中的 XML和XQuery如何如此直观有效地混合。
除了转换它检索的数据之外,XQuery在查找数据方面也比XPath好得多。 XQuery和XPath通常提供冗余,这可以帮助程序员在查询中更具表现力。 例如,清单7显示了如何将XPath表达式age lt 18
移到FLWOR表达式中的where
子句中。
let $xml:= _XML from Listing 1_
return
<ol>{
for $user in $xml//user
where $user/age lt 18
order by $user/name/last
return <li>{$user/name/last/text()}</li>
}</ol>
清单7中的表达式产生的结果与清单6中的表达式结果完全相同。 但是XQuery的where
子句在表达约束方面比XPath语法灵活得多。 XQuery where
子句可以包含任意复杂度的嵌套表达式,甚至可以包含函数调用。 XQuery不会对记录选择表达式施加限制。
尽管XPath不支持函数,但XQuery提供了大量内置函数和运算符的集合,还允许用户定义自己的函数。 XQuery函数是强类型的,支持递归,并且可以声明为内部或外部。 内部函数是标准函数,其中函数主体遵循函数声明。 外部函数是一种函数声明,它为实现打开了方便之门,以允许用户使用其他编程语言定义函数的主体。
虽然递归可能并不是许多开发人员日常执行任务的最佳方法,但是当您使用XML时,它可能会派上用场,因为XML可以包含任意嵌套的节点。 考虑清单8中定义的transform-names
函数。
(: Part 1 :)
define function transform-names($node as node()) as node() {
element{replace(name($node), "_", "-")} {
$node/text(), for $subnode in $node/* return transform-names($subnode)
}
}
(: Part 2 :)
let $xml:=
<item>
<item_type>book</item_type>
<contributors>
<author>
<first_name>Charles</first_name>
<last_name>Edward</last_name>
<home_address>
<home_street>206 S. Solomon St.</home_street>
<home_city>New Orleans</home_city>
<home_state>LA</home_state>
<home_zip>70119</home_zip>
</home_address>
</author>
<artist>
<last_name>Salinas</last_name>
</artist>
</contributors>
</item>
return transform-names($xml)
(: Result
<item>
<item-type>book</item-type>
<contributors>
<author>
<first-name>Charles</first-name>
<last-name>Edward</last-name>
<home-address>
<home-street>206 S. Solomon St.</home-street>
<home-city>New Orleans</home-city>
<home-state>LA</home-state>
<home-zip>70119</home-zip>
</home-address>
</author>
<artist>
<last-name>Salinas</last-name>
</artist>
</contributors>
</item>
:)
仅相当于清单8第1部分中出现的代码的transform-names
函数接受一个任意复杂度的XML文档或节点。 在每个XML标记名称中,该函数都将所有下划线字符(_)替换为破折号(-)。
在这种情况下,递归使函数遍历文档的结构变得微不足道。 结果,该函数简洁(三行!),易于维护,并且可以与任何不使用属性的有效XML文档或节点一起使用。 即使一开始似乎很难完全掌握该功能(特别是对于不经常使用递归的程序员),人们也可能会很快猜到如何修改该功能以删除下划线而不是用短划线代替。
XPath没有提供在查询中加入XML节点的方法。 但是,就像SQL提供了一种自然的语法来表达查询中的表联接一样,XQuery提供了一种直观的(至少对SQL用户而言)联接XML节点集的方法。 清单9中的代码描述了XQuery中联接的工作方式。
(: Part 1 :)
let $authors:=
<authors>
<author>
<name>Harold Abelson</name>
<books>
<isbn>978-0-07-000422-1</isbn>
<isbn>978-0-262-01063-4</isbn>
</books>
</author>
<author>
<name>Paul Graham</name>
<books>
<isbn>978-0-13-370875-2</isbn>
<isbn>978-0-13-030552-7</isbn>
<isbn>978-0-596-00662-4</isbn>
</books>
</author>
<author>
<name>Apostolos-Paul Refenes</name>
<books>
<isbn>978-0-471-94364-8</isbn>
<isbn>978-981-02-2819-4</isbn>
</books>
</author>
</authors>
(: Part 2 :)
let $books:=
<books>
<book>
<title>Structure and Interpretation of Computer Programs</title>
<isbn>978-0-07-000422-1</isbn>
</book>
<book>
<title>Turtle Geometry</title>
<isbn>978-0-262-01063-4</isbn>
</book>
<book>
<title>ANSI Common LISP</title>
<isbn>978-0-13-370875-2</isbn>
</book>
<book>
<title>On LISP</title>
<isbn>978-0-13-030552-7</isbn>
</book>
<book>
<title>Hackers and Painters</title>
<isbn>978-0-596-00662-4</isbn>
</book>
<book>
<title>Neural Networks in the Capital Markets</title>
<isbn>978-0-471-94364-8</isbn>
</book>
<book>
<title>Neural Networks in Financial Engineering</title>
<isbn>978-981-02-2819-4</isbn>
</book>
<book>
<title>Handbook of Artificial Intelligence</title>
<isbn>978-0-201-16889-1</isbn>
</book>
<book>
<title>Artificial Intelligence Programming</title>
<isbn>978-0-89859-609-0</isbn>
</book>
<book>
<title>A New Guide to Artificial Intelligence</title>
<isbn>978-0-89391-607-7</isbn>
</book>
<book>
<title>Artificial Intelligence</title>
<isbn>978-0-08-034112-5</isbn>
</book>
<book>
<title>Artificial Intelligence</title>
<isbn>978-0-631-18385-3</isbn>
</book>
</books>
(: Part 3 :)
return
<books-complete-info>{
for $book in $books/*
for $author in $authors/*
where $book/isbn = $author//isbn
and (
contains($book/title, "LISP")
or contains($book/title, "Neural"))
order by $book/title
return <book>{$book/*, $author/name}</book>
}</books-complete-info>
清单9的第1部分和第2部分将XML文档分配给变量author和book。 书中的某些节点与作者中的节点有关,因此,书节点的ISBN在为作者节点列出的节点中。
清单3的第3部分包含一个XQuery联接表达式,该表达式汇编一个新的XML文档books-complete-info( 清单10中向前看),其中包含带有作者姓名的书籍节点。
请注意清单9第3部分中有关代码的一些非凡之处。 该代码开头附近的两个for
表达式向XQuery暗示这将是一个联接表达式。 在概念上,where子句类似于在SQL中为实现联接而可能编写的子句。 但是请注意,一个作者节点可以具有多个ISBN,这要求where
子句有效地表示:“书的ISBN在作者的ISBN中。” 这与SQL where子句中的子选择相比要更多,但是XQuery语法似乎更加直观自然。 当然,XQuery表达式更加简洁。
<books-complete-info>
<book>
<title>ANSI Common LISP</title>
<isbn>978-0-13-370875-2</isbn>
<name>Paul Graham</name>
</book>
<book>
<title>On LISP</title>
<isbn>978-0-13-030552-7</isbn>
<name>Paul Graham</name>
</book>
<book>
<title>Neural Networks in the Capital Markets</title>
<isbn>978-0-471-94364-8</isbn>
<name>Apostolos-Paul Refenes</name>
</book>
<book>
<title>Neural Networks in Financial Engineering</title>
<isbn>978-981-02-2819-4</isbn>
<name>Apostolos-Paul Refenes</name>
</book>
</books-complete-info>
XPath是成熟的DSL,应该成为获取深埋在XML文档或存储库中的数据的首选。 但是,XPath并非旨在处理多种问题。 如您在本文中所见,XQuery极大地扩展了XPath,当您具有复杂的数据选择要求或需要返回经过排序,特殊格式化或以其他方式转换的结果时,XQuery已成为首选工具。
翻译自: https://www.ibm.com/developerworks/xml/library/x-xqueryxpath/index.html
xquery