尧图精选

XSLT排序实战:彻底搞懂<xsl:sort>标签用法与避坑指南

🕒 发布时间:2026/10/1 3:33:17 📁 来源:尧图网络
前阵子帮人修一个老项目数据源是 XML页面靠 XSLT 在服务端生成。问题很典型XML 里的记录顺序是乱的前端一个for-each直接把节点遍历输出表格数据就跟着文档顺序走结果报表看起来像随手甩上去的。这种场景下XSLT 里专门负责排序的xsl:sort标签就是核心武器。这篇文章我就把xsl:sort从挂载位置、属性细节到实战踩坑完整拆一遍适合正在写 XSLT 模板、或者在老系统里维护数据导出的朋友参考。1. 先搞明白sort 标签到底解决什么问题1.1 没有 sort 时输出顺序由什么决定XSLT 对 XML 的处理有一个底层规则默认情况下节点输出顺序就是 XML 文档顺序document order即节点在源文件里出现的先后顺序。你写xsl:for-each selectstaff/employee它就会从上到下把employee节点挨个取出来压根不看里面的值是什么。这在小数据量下没什么感觉但一到真实业务就露馅了。比如 CRM 导出的客户列表、订单流水、日志记录很多源 XML 本身是数据采集端拼出来的顺序完全是写入顺序而不是业务想要的顺序。你想按金额从大到小展示或者按时间倒序排列直接对节点集操作是做不到的必须让 XSLT 处理器先把节点重新排序然后再进入输出流程——这就是xsl:sort的角色。1.2 sort 的工作位置它是 for-each 和 apply-templates 的指挥棒xsl:sort不是一个独立的顶层模板元素它不能单独出现在某个xsl:template里向输出流写东西。它是一个修饰性指令只能挂在两个地方xsl:for-each内部xsl:apply-templates内部位置必须紧跟在所在元素的开头也就是在这两个指令的所有其他子元素之前。它就像队伍前面那个叫号员for-each和apply-templates负责按照规则抓一批节点sort则决定这批节点以什么顺序走到你面前。理解了这层关系后面所有用法都是围绕先选节点再排顺序最后输出这个流程展开的。提示如果你把xsl:sort写在xsl:for-each中间比如先写了一个xsl:value-of再写xsl:sortXSLT 处理器会直接报编译错误而不是忽略它。这一点在第六章《排序失效排查手册》里再细说。2. 最常用的两种挂载方式for-each 里的正序与倒序2.1 最小可运行示例按 hireDate 排员工名单先看最常见的场景用for-each遍历节点并排序。假设 XML 结构是这样的?xml version1.0 encodingUTF-8? staff employee name张伟/name department研发部/department salary12000/salary hireDate2021-03-15/hireDate /employee employee name李娜/name department市场部/department salary9500/salary hireDate2020-11-02/hireDate /employee employee name王强/name department研发部/department salary15000/salary hireDate2019-07-08/hireDate /employee /staff如果直接for-each输出顺序就是张伟、李娜、王强。但我想按入职日期从早到晚排就写成?xml version1.0 encodingUTF-8? xsl:stylesheet version1.0 xmlns:xslhttp://www.w3.org/1999/XSL/Transform xsl:template match/ ul xsl:for-each selectstaff/employee xsl:sort selecthireDate orderascending/ li xsl:value-of selectconcat(name, 入职于 , hireDate)/ /li /xsl:for-each /ul /xsl:template /xsl:stylesheet输出顺序变成王强2019-07-08、李娜2020-11-02、张伟2021-03-15。这里有一个关键逻辑selecthireDate表示用当前节点employee下的 hireDate 子元素的值作为排序键。XPath 路径是相对for-each选中的当前节点来解析的不是相对根节点。这一点新手特别容易写错比如在排序键里写成select/staff/employee/hireDate虽然也能选到值但含义完全不同而且性能差一大截。2.2 倒序的写法与常见错用倒序就是把order属性改成descendingxsl:for-each selectstaff/employee xsl:sort selectsalary>xsl:template match/ xsl:apply-templates selectstaff/employee/ /xsl:template xsl:template matchemployee p xsl:value-of selectconcat(name, , salary)/ /p /xsl:template这段代码的输出顺序与 XML 文档顺序保持一致无法实现按工资排队。3.2 用 sort 控制节点处理顺序解决方式是在apply-templates内部挂sortxsl:template match/ xsl:apply-templates selectstaff/employee xsl:sort selectsalary>xsl:sort selectsalary>xsl:sort selectname langzh-CN/想法是让中文按拼音或者笔画排。但我要泼一盆冷水XSLT 1.0 里的lang能否真正实现中文排序完全取决于你用的处理器。浏览器内置的 XSLT 引擎比如老版 IE基本不认zh-CNxsltproclibxslt也要看系统 locale 有没有装中文支持Saxon 对 locale 的支持倒是比较完整但 XSLT 1.0 时代的 Saxon 中文排序也谈不上好用。lang属性最常见的结局是处理器忽略它退化成按 Unicode 码点排序。而汉字在 Unicode 里的顺序和拼音没有任何关系所以你会看到张排到李前面这类完全没道理的结果。稳妥的做法是在源 XML 里增加拼音字段或者预处理时转成拼音首字母作为排序键。我在后面第五章实战里会给出一个更通用的方案。4.4 多级排序多个 sort 标签的优先级业务里几乎没有只按一个字段排的需求。最常见的是先按部门排再按工资排最后按姓名排。XSLT 的多级排序非常简单在同一个for-each或apply-templates里连续写多个xsl:sort顺序就是优先级顺序。xsl:for-each selectstaff/employee xsl:sort selectdepartment orderascending/ xsl:sort selectsalary>xsl:sort selecttranslate(level, 高中低, 123)>?xml version1.0 encodingUTF-8? staff employee name张伟/name department研发部/department salary12000/salary hireDate2021-03-15/hireDate city北京/city /employee employee name李娜/name department市场部/department salary9500/salary hireDate2020-11-02/hireDate city上海/city /employee employee name王强/name department研发部/department salary15000/salary hireDate2019-07-08/hireDate city深圳/city /employee employee name赵敏/name department财务部/department salary8000/salary hireDate2022-01-20/hireDate city北京/city /employee employee name陈杰/name department市场部/department salary10000/salary hireDate2018-05-30/hireDate city广州/city /employee employee name刘洋/name department研发部/department salary13000/salary hireDate2020-06-18/hireDate city杭州/city /employee /staff5.2 完整的 XSLT 模板和输出结果目标按部门升序部门内部按工资降序工资相同时按姓名升序。输出成 HTML 表格?xml version1.0 encodingUTF-8? xsl:stylesheet version1.0 xmlns:xslhttp://www.w3.org/1999/XSL/Transform xsl:output methodhtml encodingUTF-8/ xsl:template match/ html headtitle员工工资报表/title/head body table border1 tr th部门/th th姓名/th th工资/th /tr xsl:for-each selectstaff/employee xsl:sort selectdepartment orderascending/ xsl:sort selectsalary>xsl:for-each selectstaff/employee xsl:value-of selectsalary/ xsl:sort selectsalary>xsl:variable namesortedEmployees xsl:for-each selectstaff/employee xsl:sort selectsalary>
上一篇/下一篇内容由系统自动关联 返回资讯列表 →