---
title: "使用内置的 XPath 函数"
date: "2011-07-12"
categories: 
  - "xpath"
tags: 
  - "altova-online-training"
  - "altova-xmlspy"
  - "data-mapping"
  - "education"
  - "learn-xml"
  - "xml-editor"
  - "xmlspy"
  - "xpath"
  - "xslt"
description: 本文探讨了如何使用 XPath 函数有效地对 XML 数据进行排序，重点介绍了提取姓氏以及处理标题中的冠词。
---
Status: #blog

Tags:  #altova-online-training #altova-xmlspy #data-mapping #education #learn-xml #xml-editor #xmlspy #xpath #xslt

Categories: [xpath+xquery](/blog/zh/category/xpathxquery.md)
# 使用内置的 XPath 函数

在开发一个[Altova在线培训](https://www.altova.com/zh/aot/online-training.aspx)课程时，我按照作者对书籍进行排序。我发现我的“作者”字段是一个包含作者全名的字符串，因此书籍是按照字符串的第一个字母，也就是作者的名字的第一个字母进行排序的。为了使排序更合理，我没有在课程中进行修改，但您可以使用XPath函数轻松地从字符串中提取姓氏，并将其用作排序的关键。如果随后您使用书籍的标题作为辅助排序的关键，您可能会遇到一个问题：那些以“A”、“An”或“The”开头的标题。我希望使用标题作为辅助排序的关键，但忽略开头的定冠词或不定冠词。

![使用 XPath 表达式对书籍列表进行排序，并输出排序后的结果](https://lh4.ggpht.com/-OBe9_xfjlpQ/TgD1tUSi6sI/AAAAAAAAAGw/nLwMwUDQTJ4/correct-sort-xsl2_thumb%25255B2%25255D.png?imgmax=800) 

让我们来看看我们是如何编写这段XSLT代码的。本文的编写使用了[XMLSpy](https://www.altova.com/zh/xmlspy.html)作为平台，但相同的XPath表达式也可以在[MapForce](https://www.altova.com/zh/mapforce.html)或[StyleVision](https://www.altova.com/zh/stylevision.html)中使用，以达到类似的效果。我们可以从一个简单的XML书籍列表开始。我们有4本书，每本书包含作者和书名节点。 

[![三本书的清单](https://lh5.ggpht.com/-oP0T7tap1j8/TgD1qZFjeJI/AAAAAAAAAGI/Q9N1sefXpr8/book-list-xml2_thumb%25255B6%25255D.png?imgmax=800 "book_list.xml")](http://lh5.ggpht.com/-1XkQTBtjxgI/TgD1p7e9jRI/AAAAAAAAAGE/r5x-fYqrXB4/s1600-h/book-list-xml2%25255B8%25255D.png) 

一个用于创建书籍列表的XSLT代码可能如下所示：

[![输出图书列表，不进行排序](https://lh6.ggpht.com/-Xl2drxDYvWk/TgD1qyCS-tI/AAAAAAAAAGQ/XtlE-ha3aPY/no-sort-xsl2_thumb%25255B3%25255D.png?imgmax=800 "no_sort.xsl")](http://lh6.ggpht.com/-0QjJb5fadso/TgD1qitzcrI/AAAAAAAAAGM/ENp-px5_e1g/s1600-h/no-sort-xsl2%25255B7%25255D.png) 

这将产生以下输出： 

[![未分类的书籍列表](https://lh4.ggpht.com/-84ZeFnOVnX0/TgD1ruHFFII/AAAAAAAAAGY/rtui-dgtCUQ/no-sort-output_thumb%25255B2%25255D.png?imgmax=800 "Output from XSL with No Sort")](http://lh6.ggpht.com/-DcGmqHHaQi4/TgD1rJWB_GI/AAAAAAAAAGU/V9i4088XoKs/s1600-h/no-sort-output%25255B4%25255D.png) 

这些书籍的输出顺序与原始数据文件中出现的顺序一致。如果我们在 `xsl:for-each` 循环中添加 `xsl:sort`，我们就可以以其他方式组织输出结果。 

[![输出带有基本排序的书籍列表](https://lh4.ggpht.com/-JX9qbo9fYjo/TgD1sKu_YVI/AAAAAAAAAGg/rtHXnh_x94o/sort-xsl2_thumb%25255B2%25255D.png?imgmax=800 "sort.xsl")](http://lh4.ggpht.com/-WEIuCC1NRNw/TgD1rzIgzPI/AAAAAAAAAGc/q79Va9UWMgo/s1600-h/sort-xsl2%25255B4%25255D.png) 

这会生成一个排序后的列表，但排序结果可能不正确。 

[![使用 XSL 进行基本排序后的输出结果](https://lh3.ggpht.com/-kN0aSY1DhE8/TgD1spmsmLI/AAAAAAAAAGo/BsxQW6XYwWk/sort-output_thumb%25255B2%25255D.png?imgmax=800 "Output from XSL with Basic Sort")](http://lh3.ggpht.com/-2JAA_7dIt4o/TgD1sYIFl-I/AAAAAAAAAGk/M5g2B1vVC_M/s1600-h/sort-output%25255B4%25255D.png) 

将作者按照字符串排序，会导致“Jules Verne”排在“Mark Twain”之前。同样，“A Connecticut Yankee in King Arthur’s Court”也会排在“Adventures of Huckleberry Finn”之前。我们希望忽略不定冠词“A”，以便“Adventures of Huckleberry Finn”排在“A Connecticut Yankee in King Arthur’s Court”之前。我们可以使用XPath表达式来提取我们需要的排序键。 

[![使用 XPath 表达式对书籍列表进行排序，并输出排序后的结果](https://lh4.ggpht.com/-OBe9_xfjlpQ/TgD1tUSi6sI/AAAAAAAAAGw/nLwMwUDQTJ4/correct-sort-xsl2_thumb%25255B2%25255D.png?imgmax=800 "correct_sort.xsl")](http://lh4.ggpht.com/-1797dSMl6G0/TgD1tIvfZnI/AAAAAAAAAGs/G8i7ztztp9Y/s1600-h/correct-sort-xsl2%25255B4%25255D.png) 

在查看输出结果之前，我们先来看看代码。我们用“reverse(tokenize(author, ' '))[1]”替换了“author”。`tokenize`函数使用单个空格作为分隔符，将作者字符串分解成独立的词语。例如，“Jules Verne”会被分解成“Jules”和“Verne”。`reverse`函数则反转这些词语的顺序，变为“Verne”和“Jules”。方括号“[1]”选择列表中第一个元素，即“Verne”。这个值被用于`xsl:sort`函数，用于对书籍进行排序。虽然这并非完美的解决方案，但在我们的案例中，它能够正常工作。标题看起来有些复杂，但其逻辑非常简单。表达式“tokenize(title, ' ')[1]”提取了标题的第一个词。因此，第一个条件判断是“标题的第一个词是否是“A”？”。如果是，则返回标题从第三个字母开始的子字符串，从而消除了“A”和后面的空格。如果标题的第一个词不是“A”，则需要再次进行判断，看标题的第一个词是否是“The”。如果是，则使用标题从第五个字符开始的子字符串，从而消除了“The”和后面的空格。如果两个判断都失败，则直接将标题作为排序的关键字。我们可以在代码中添加另一个判断，看第一个词是否是“An”，但对于这个数据集，这并非必要。执行这个最后的XSLT，我们得到以下输出结果。 

[![使用修正后的排序规则生成的XSL输出](https://lh5.ggpht.com/-78G5M2Z81Ow/TgD1uRmfiQI/AAAAAAAAAG4/YStPJ1HRDj0/correct-sort-output_thumb%25255B2%25255D.png?imgmax=800 "Output from XSL with Corrected Sort")](http://lh3.ggpht.com/-h5AD7I5C60o/TgD1tsVI9fI/AAAAAAAAAG0/Nj8_cO10i2s/s1600-h/correct-sort-output%25255B4%25255D.png) 

“马克·吐温”现在排在“儒勒·凡尔纳”之前。“哈克贝利·费恩历险记”排在“卡拉韦拉斯县的著名跳蛙”和“亚瑟王宫廷里的康涅狄格人”之前。我们处理作者名称时的一个问题是，我们希望将“儒勒·凡尔纳”视为“凡尔纳, 儒勒”来进行排序，这样如果存在一本由“吉米·凡尔纳”所著的书，排序系统会将它们视为不同的作者。但我们的代码目前无法做到这一点。使用“concat(reverse(tokenize(author, ‘ ‘))\[1\], reverse(tokenize(author, ‘ ‘))\[2\])”可以正确地对“儒勒·凡尔纳”和“吉米·凡尔纳”进行排序，但这个解决方案仅适用于双字名称。如果作者的姓名带有后缀（例如“马丁·路德·金三世”）或包含多个词语（例如“乔治·赫伯特·沃克·布什”），则代码将无法正常工作。在姓名排序方面，存在许多与通用规则相悖的情况，而要支持所有可能的变体，所需的代码远远超出了本文的范围。我们想要展示的是，如何使用XPath表达式实时操作XML数据。虽然我们有时无法完全控制数据源的格式，但通过利用XPath表达式的强大功能，我们可以将数据转换成所需的格式。这些示例中使用的文件的副本可以在[此处](https://www.altova.com/zh/library/Using_XPath_Functions.zip)获取。
