使用内置的 XPath 函数
在开发一个Altova在线培训课程时,我按照作者对书籍进行排序。我发现我的“作者”字段是一个包含作者全名的字符串,因此书籍是按照字符串的第一个字母,也就是作者的名字的第一个字母进行排序的。为了使排序更合理,我没有在课程中进行修改,但您可以使用XPath函数轻松地从字符串中提取姓氏,并将其用作排序的关键。如果随后您使用书籍的标题作为辅助排序的关键,您可能会遇到一个问题:那些以“A”、“An”或“The”开头的标题。我希望使用标题作为辅助排序的关键,但忽略开头的定冠词或不定冠词。
![]()
让我们来看看我们是如何编写这段XSLT代码的。本文的编写使用了XMLSpy作为平台,但相同的XPath表达式也可以在MapForce或StyleVision中使用,以达到类似的效果。我们可以从一个简单的XML书籍列表开始。我们有4本书,每本书包含作者和书名节点。
![]()
一个用于创建书籍列表的XSLT代码可能如下所示:
![]()
这将产生以下输出:
![]()
这些书籍的输出顺序与原始数据文件中出现的顺序一致。如果我们在 xsl:for-each 循环中添加 xsl:sort,我们就可以以其他方式组织输出结果。
![]()
这会生成一个排序后的列表,但排序结果可能不正确。
![]()
将作者按照字符串排序,会导致“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表达式来提取我们需要的排序键。
![]()
在查看输出结果之前,我们先来看看代码。我们用“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,我们得到以下输出结果。
![]()
“马克·吐温”现在排在“儒勒·凡尔纳”之前。“哈克贝利·费恩历险记”排在“卡拉韦拉斯县的著名跳蛙”和“亚瑟王宫廷里的康涅狄格人”之前。我们处理作者名称时的一个问题是,我们希望将“儒勒·凡尔纳”视为“凡尔纳, 儒勒”来进行排序,这样如果存在一本由“吉米·凡尔纳”所著的书,排序系统会将它们视为不同的作者。但我们的代码目前无法做到这一点。使用“concat(reverse(tokenize(author, ‘ ‘))[1], reverse(tokenize(author, ‘ ‘))[2])”可以正确地对“儒勒·凡尔纳”和“吉米·凡尔纳”进行排序,但这个解决方案仅适用于双字名称。如果作者的姓名带有后缀(例如“马丁·路德·金三世”)或包含多个词语(例如“乔治·赫伯特·沃克·布什”),则代码将无法正常工作。在姓名排序方面,存在许多与通用规则相悖的情况,而要支持所有可能的变体,所需的代码远远超出了本文的范围。我们想要展示的是,如何使用XPath表达式实时操作XML数据。虽然我们有时无法完全控制数据源的格式,但通过利用XPath表达式的强大功能,我们可以将数据转换成所需的格式。这些示例中使用的文件的副本可以在此处获取。