XSLT分组主要有两种方式:XSLT 2.0+使用for-each-group指令,通过group-by等属性实现直观高效的分组;XSLT 1.0则依赖Muenchian Grouping,利用key()和generate-id()筛选每组首个节点,虽复杂但有效。

XSLT对节点进行分组操作,核心上来说,主要有两种主流方式:对于XSLT 2.0及更高版本,我们有强大的
for-each-group
key()
generate-id()
要对XSLT中的节点进行分组,我们通常会根据某个节点的特定属性值、子节点内容或者计算出的某个键值来组织相关的节点集合。这在数据转换中非常常见,比如把扁平化的数据转换成带有层级结构的报告。
1. XSLT 2.0+ 中的 for-each-group
这是现代XSLT处理分组的首选方式,它直观且强大。
for-each-group
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<grouped-data>
<!-- 根据'category'属性进行分组 -->
<xsl:for-each-group select="item" group-by="@category">
<category-group name="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<item-detail id="{@id}">
<name><xsl:value-of select="name"/></name>
<price><xsl:value-of select="price"/></price>
</item-detail>
</xsl:for-each>
</category-group>
</xsl:for-each-group>
</grouped-data>
</xsl:template>
</xsl:stylesheet>对应这个XML输入:
<root>
<item id="A001" category="Electronics">
<name>Laptop</name>
<price>1200</price>
</item>
<item id="A002" category="Books">
<name>XSLT Cookbook</name>
<price>45</price>
</item>
<item id="A003" category="Electronics">
<name>Mouse</name>
<price>25</price>
</item>
<item id="A004" category="Books">
<name>XML Basics</name>
<price>30</price>
</item>
</root>输出会是:
<grouped-data>
<category-group name="Electronics">
<item-detail id="A001">
<name>Laptop</name>
<price>1200</price>
</item-detail>
<item-detail id="A003">
<name>Mouse</name>
<price>25</price>
</item-detail>
</category-group>
<category-group name="Books">
<item-detail id="A002">
<name>XSLT Cookbook</name>
<price>45</price>
</item-detail>
<item-detail id="A004">
<name>XML Basics</name>
<price>30</price>
</item-detail>
</category-group>
</grouped-data>group-by
current-grouping-key()
current-group()
2. XSLT 1.0 中的 Muenchian Grouping(键控分组)
在没有
for-each-group
key()
generate-id()
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- 定义一个键,用于按'category'属性查找item节点 -->
<xsl:key name="items-by-category" match="item" use="@category"/>
<xsl:template match="/root">
<grouped-data>
<!-- 遍历所有item节点,但只选择每个分组的第一个节点 -->
<xsl:for-each select="item[count(. | key('items-by-category', @category)[1]) = 1]">
<xsl:variable name="currentCategory" select="@category"/>
<category-group name="{$currentCategory}">
<!-- 遍历当前分组的所有节点 -->
<xsl:for-each select="key('items-by-category', $currentCategory)">
<item-detail id="{@id}">
<name><xsl:value-of select="name"/></name>
<price><xsl:value-of select="price"/></price>
</item-detail>
</xsl:for-each>
</category-group>
</xsl:for-each>
</grouped-data>
</xsl:template>
</xsl:stylesheet>使用与上面相同的XML输入,输出结果会是一致的。这里的
item[count(. | key('items-by-category', @category)[1]) = 1]for-each-group
老实说,自从XSLT 2.0引入
for-each-group
for-each-group
select
select="item"
item
group-by
group-starting-with
<h3>
<p>
<h3>
group-ending-with
group-starting-with
group-adjacent
group-by
在
for-each-group
current-group()
current-grouping-key()
举个更复杂的例子,我们想分组销售订单,先按年份,再按月份:
XML输入:
PHP是一种功能强大的网络程序设计语言,而且易学易用,移植性和可扩展性也都非常优秀,本书将为读者详细介绍PHP编程。 全书分为预备篇、开始篇和加速篇三大部分,共9章。预备篇主要介绍一些学习PHP语言的预备知识以及PHP运行平台的架设;开始篇则较为详细地向读者介绍PKP语言的基本语法和常用函数,以及用PHP如何对MySQL数据库进行操作;加速篇则通过对典型实例的介绍来使读者全面掌握PHP。 本书
472
<sales> <order id="1" date="2023-01-15" amount="100"/> <order id="2" date="2023-02-20" amount="150"/> <order id="3" date="2023-01-25" amount="120"/> <order id="4" date="2022-11-01" amount="80"/> <order id="5" date="2023-02-10" amount="200"/> </sales>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/sales">
<yearly-sales>
<!-- 第一层分组:按年份 -->
<xsl:for-each-group select="order" group-by="substring(@date, 1, 4)">
<year-group year="{current-grouping-key()}">
<!-- 第二层分组:在当前年份组内,再按月份分组 -->
<xsl:for-each-group select="current-group()" group-by="substring(@date, 6, 2)">
<month-group month="{current-grouping-key()}">
<total-amount><xsl:value-of select="sum(current-group()/@amount)"/></total-amount>
<orders-in-month>
<xsl:for-each select="current-group()">
<order-summary id="{@id}" date="{@date}" amount="{@amount}"/>
</xsl:for-each>
</orders-in-month>
</month-group>
</xsl:for-each-group>
</year-group>
</xsl:for-each-group>
</yearly-sales>
</xsl:template>
</xsl:stylesheet>这个例子展示了嵌套分组的强大。我们先按年份
substring(@date, 1, 4)
current-group()
substring(@date, 6, 2)
for-each-group
Muenchian Grouping,这个名字听起来有点酷,但它的实现方式,对于初学者来说,绝对是XSLT 1.0时代的一个“智力挑战”。它不是一个内置指令,而是一种巧妙地利用XSLT 1.0固有功能的模式。我记得刚开始学XSLT 1.0的时候,理解这个模式花了我不少时间,因为它确实有点反直觉。
核心思想:
xsl:key
xsl:key
key()
count(. | key('your-key', your-criteria)[1]) = 1key('your-key', your-criteria)your-criteria
key('your-key', your-criteria)[1]count(. | ...)
= 1
key()
我们还是用之前的商品分类XML来演示Muenchian Grouping:
<root>
<item id="A001" category="Electronics">
<name>Laptop</name>
<price>1200</price>
</item>
<item id="A002" category="Books">
<name>XSLT Cookbook</name>
<price>45</price>
</item>
<item id="A003" category="Electronics">
<name>Mouse</name>
<price>25</price>
</item>
<item id="A004" category="Books">
<name>XML Basics</name>
<price>30</price>
</item>
</root>XSLT 1.0 (Muenchian Grouping):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- 定义一个键,用于按'category'属性查找item节点 -->
<xsl:key name="items-by-category" match="item" use="@category"/>
<xsl:template match="/root">
<grouped-data>
<!-- 遍历所有item节点,但只选择每个分组的第一个节点 -->
<xsl:for-each select="item[count(. | key('items-by-category', @category)[1]) = 1]">
<xsl:variable name="currentCategory" select="@category"/>
<category-group name="{$currentCategory}">
<!-- 遍历当前分组的所有节点 -->
<xsl:for-each select="key('items-by-category', $currentCategory)">
<item-detail id="{@id}">
<name><xsl:value-of select="name"/></name>
<price><xsl:value-of select="price"/></price>
</item-detail>
</xsl:for-each>
</category-group>
</xsl:for-each>
</grouped-data>
</xsl:template>
</xsl:stylesheet>局限性:
item[count(. | key('items-by-category', @category)[1]) = 1]for-each-group
key()
key()
group-starting-with
尽管有这些局限性,但对于那些仍然运行在XSLT 1.0环境下的系统来说,Muenchian Grouping依然是不可或缺的技能。它证明了即使在语言特性有限的情况下,开发者也能通过巧妙的组合实现复杂的功能。
XSLT的分组能力远不止是简单地按一个字段值来归类。在实际项目中,我遇到过各种稀奇古怪的分组需求,有些真的需要跳出常规思维去解决。这正是XSLT的魅力所在,它提供了一套工具集,让你能像搭积木一样,构建出满足特定业务逻辑的转换。
连续兄弟节点分组(group-starting-with
for-each-group
<h2>
<p>
<h2>
<h2>
<p>
<h2>
XML输入 (模拟HTML片段):
<document> <h2>Section A</h2> <p>Content for A, paragraph 1.</p> <p>Content for A, paragraph 2.</p> <h2>Section B</h2> <p>Content for B, paragraph 1.</p> <ul><li>List item 1</li><li>List item 2</li></ul> <p>Content for B, paragraph 2.</p> <h2>Section C</h2> <p>Content for C.</p> </document>
XSLT (使用 group-starting-with
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/document">
<sections>
<!-- 以h2节点作为新组的开始 -->
<xsl:for-each-group select="*" group-starting-with="h2">
<section title="{current-group()[1]}"> <!-- current-group()[1]是h2节点 -->
<content>
<xsl:copy-of select="current-group()[position() > 1]"/> <!-- 复制h2后面的所有内容 -->
</content>
</section>
</xsl:for-each-group>
</sections>
</xsl:template>
</xsl:stylesheet>这里,
group-starting-with="h2"
<h2>
<h2>
<h2>
基于动态或计算值的分组 有时候,分组的依据不是一个简单的属性值,而是一个需要计算出来的结果。比如,我们想把商品按照价格区间(0-50, 51-100, 101-200等)来分组。
XSLT (计算价格区间):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<price-ranges>
<xsl:for-each-group select="item" group-by="floor(以上就是XSLT如何对节点进行分组操作?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号