XSLT中嵌套for-each循环继续主循环或从头遍历实现版本数据分组
XSLT嵌套循环遍历问题解决方案
你的需求本质是按ver字段对Row节点分组输出,两种遍历范围的实现方式如下:
首先修正原代码的语法错误
你现有代码存在几个语法问题会直接导致运行失败:
- XML节点名大小写敏感:输入的节点是
<Field>,XPath中写小写field会匹配不到 xsl:if的判断属性为test,不是select- 仅属性值动态插值需要用
{},变量定义、XPath表达式中不需要加{} - 部分XPath表达式少闭合方括号,例如
field[@name='ver'}应改为Field[@name='ver']
方案1:内层从XML起始位置遍历
内层for-each的select直接写根路径定位所有Row节点,再用currentVer过滤同版本节点即可:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <Main> <xsl:for-each select="table/Row"> <xsl:if test="Field[@name='isNewVersion'] = 1"> <Data ver="{Field[@name='ver']}"> <xsl:variable name="currentVer" select="Field[@name='ver']" /> <!-- 内层从根开始遍历所有Row,过滤同版本 --> <xsl:for-each select="/table/Row[Field[@name='ver'] = $currentVer]"> <Sub id="{Field[@name='sub']}" /> </xsl:for-each> </Data> </xsl:if> </xsl:for-each> </Main> </xsl:template> </xsl:stylesheet>
方案2:内层从外层中断位置继续遍历
如果你的输入XML中同版本的Row节点是连续排列的(和你给出的测试数据结构一致),可以用以下写法只遍历后续同版本节点,执行效率更高:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <Main> <xsl:for-each select="table/Row"> <xsl:if test="Field[@name='isNewVersion'] = 1"> <Data ver="{Field[@name='ver']}"> <xsl:variable name="currentVer" select="Field[@name='ver']" /> <!-- 包含当前Row + 后续所有同版本Row --> <xsl:for-each select="current() | following-sibling::Row[Field[@name='ver'] = $currentVer]"> <Sub id="{Field[@name='sub']}" /> </xsl:for-each> </Data> </xsl:if> </xsl:for-each> </Main> </xsl:template> </xsl:stylesheet>
更高性能的推荐方案:Muenchian分组
如果XML数据量较大,嵌套循环的性能会偏低,XSLT 1.0标准的分组实现为Muenchian分组,代码如下:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="rowByVer" match="Row" use="Field[@name='ver']" /> <xsl:template match="/"> <Main> <xsl:for-each select="table/Row[count(. | key('rowByVer', Field[@name='ver'])[1]) = 1]"> <Data ver="{Field[@name='ver']}"> <xsl:for-each select="key('rowByVer', Field[@name='ver'])"> <Sub id="{Field[@name='sub']}" /> </xsl:for-each> </Data> </xsl:for-each> </Main> </xsl:template> </xsl:stylesheet>
以上三种方案输出的结果都和你预期的结构完全一致。
内容的提问来源于stack exchange,提问作者korkotyan
相关产品推荐
相关产品推荐

