XSLT:带条件的apply-templates内输出匹配节点的正确序号
解决XSLT中过滤后节点连续序号问题
问题说明
匹配property节点的table-group模式模板时,需要输出符合条件节点的连续序号,但直接使用position()会返回原节点在集合中的位置,而非过滤后的连续序号,导致输出序号不连续。
当前XSLT代码
<xsl:template match="property" mode="table-group"> <xsl:variable name="property_id" select="@id" /> <xsl:if test="/shop//shop_group[@id=$group]/property_value[property_id = $property_id]/value != 0 and /shop//shop_group[@id=$group]/property_value[property_id = $property_id]/value != '' "> <tr> <td> <b><xsl:value-of select="position()" /></b> <xsl:value-of disable-output-escaping="yes" select="name"/> </td> <xsl:apply-templates select="/shop//shop_group[@id=$group]/property_value[property_id = $property_id]" mode="table-group"/> </tr> </xsl:if> </xsl:template>
示例XML
<shop> <shop_groups_properties> <property id="01"> <name>NameA</name> </property> <property id="02"> <name>NameB</name> </property> <property id="03"> <name>NameC</name> </property> <property id="04"> <name>NameD</name> </property> <property id="05"> <name>NameE</name> </property> </shop_groups_properties> <shop_group> <property_value> <property_id>01</property_id> <value>123</value> </property_value> <property_value> <property_id>02</property_id> <value>0</value> </property_value> <property_value> <property_id>03</property_id> <value></value> </property_value> <property_value> <property_id>04</property_id> <value>123</value> </property_value> <property_value> <property_id>05</property_id> <value>321</value> </property_value> </shop_group> </shop>
期望输出
<tr><td>1 NameA</td><td>123</td></tr> <tr><td>2 NameD</td><td>123</td></tr> <tr><td>3 NameE</td><td>321</td></tr>
实际错误输出
<tr><td>1 NameA</td><td>123</td></tr> <tr><td>4 NameD</td><td>123</td></tr> <tr><td>5 NameE</td><td>321</td></tr>
解决方案
方案1:先过滤节点集合再遍历(推荐)
问题根源在于xsl:if只是跳过不符合条件的节点,但position()是基于原始property节点集合的位置。因此可以先筛选出符合条件的property节点,再应用模板,此时position()会返回过滤后的连续序号。
- 修改调用模板的代码,先过滤符合条件的节点:
<xsl:apply-templates select="/shop/shop_groups_properties/property[ /shop/shop_group/property_value[property_id=current()/@id]/value != 0 and /shop/shop_group/property_value[property_id=current()/@id]/value != '' ]" mode="table-group"/>
- 简化模板,移除
xsl:if:
<xsl:template match="property" mode="table-group"> <xsl:variable name="property_id" select="@id" /> <tr> <td> <b><xsl:value-of select="position()" /></b> <xsl:value-of disable-output-escaping="yes" select="name"/> </td> <xsl:apply-templates select="/shop/shop_group/property_value[property_id = $property_id]" mode="table-group"/> </tr> </xsl:template>
方案2:在模板内部计算过滤后的序号
如果无法修改调用模板的逻辑,可在模板内部计算当前节点在所有符合条件节点中的位置:
<xsl:template match="property" mode="table-group"> <xsl:variable name="property_id" select="@id" /> <xsl:variable name="valid-condition" select="/shop/shop_group/property_value[property_id = $property_id]/value != 0 and /shop/shop_group/property_value[property_id = $property_id]/value != ''" /> <xsl:if test="$valid-condition"> <tr> <td> <b> <!-- 计算当前节点之前符合条件的节点数量,再加1得到连续序号 --> <xsl:value-of select="count(preceding-sibling::property[ /shop/shop_group/property_value[property_id=@id]/value != 0 and /shop/shop_group/property_value[property_id=@id]/value != '' ]) + 1" /> </b> <xsl:value-of disable-output-escaping="yes" select="name"/> </td> <xsl:apply-templates select="/shop/shop_group/property_value[property_id = $property_id]" mode="table-group"/> </tr> </xsl:if> </xsl:template>
说明
- 方案1性能更优,逻辑更简洁,优先推荐。
- 方案2适合无法调整调用逻辑的场景,通过遍历前置节点计数得到序号。
内容的提问来源于stack exchange,提问作者GoldenPumaMary
相关产品推荐
相关产品推荐

