递归:将复杂的条件和排序结合起来
Recursion: integrating complex condition and order by rank
现有的 XSLT 代码需要根据以下条件进行增强。
1 - 与递归算法一起添加检查每个比较行(元素)是否具有:
- 属性状态=0
- 并且属性 status2 为 null(这里意味着元素中没有属性 status2,实际上)
(只有满足这两个条件才能创建链)
2 - 排名顺序。所有恢复(或恢复)的链最终都必须按 'rank' 属性排序
(它可能与本机递归匹配,但有时不匹配。无论如何 'rank' 属性具有更高的优先级。
Rank 没有固定的命名法(如 1-2-3-4),但它的链可以通过比较 'more'-'less' 来计算。 (1 < 3 < 7)。
源代码
<A>
<X id="top" text="first" text2="*" status="0" rank="1"/>
<X id="middle" id-parent="top" text="second" text2="**" status="0" rank="3"/>
<X id="bottom" id-parent="middle" text="third" text2="***" status="0" rank="6"/>
<X id="bottom2" id-parent="middle" text="fourth" text2="****" status="0" rank="2"/> <!--note rank! bottom and middle should be switching because "bottom2 has a "higher" rank-->
<X id="bottom3" id-parent="middle" text="fifth" text2="*****" status="2" rank="6" status2="any-value-make-its-status-not_null"/>
</A>
XSLT 转换
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="ref" match="X" use="@id"/>
<xsl:template match="X">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:variable name="Xs">
<xsl:call-template name="XATT">
<xsl:with-param name="currentX" select="."/>
<xsl:with-param name="X" select=".">
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$Xs[node()]">
<xsl:attribute name="chain-text">
<xsl:for-each select="$Xs/X">
<xsl:if test="position() ne 1"><xsl:text> | </xsl:text></xsl:if>
<xsl:value-of select="concat(@text, ' ', @text2)"/>
</xsl:for-each>
</xsl:attribute>
<xsl:for-each select="$Xs/X">
<xsl:attribute name="level-{position()}"><xsl:value-of select="concat(@text, ' | ', @text2)"/></xsl:attribute>
</xsl:for-each>
</xsl:if>
</xsl:copy>
</xsl:template>
<xsl:template name="XATT">
<xsl:param name="currentX"/>
<xsl:param name="X"/>
<xsl:choose>
<xsl:when test="$currentX/@id-parent">
<xsl:call-template name="XATT">
<xsl:with-param name="currentX" select="key('ref', $currentX/@id-parent)"/>
<xsl:with-param name="X">
<xsl:copy-of select="key('ref', $currentX/@id-parent)"/>
<xsl:copy-of select="$X"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$X"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94AcskR
互联scheme
期望输出
<?xml version="1.0" encoding="UTF-8"?><A>
<X id="top" text="first" text2="*" chain-text="first *" level-1="first | *"/>
<X id="middle" id-parent="top" text="second" text2="**" chain-text="first * | second **" level-1="first | *" level-2="second | **"/>
<X id="bottom" id-parent="middle" text="third" text2="***" chain-text="first * | second ** | third ***" level-1="first | *" level-2="second | **" level-3="third | ***"/>
<!--new conditions are clearly visible on the following lines-->
<X id="bottom2" id-parent="middle" text="fourth" text2="****" chain-text="first * | fourth **** | second **" level-1="first | *" level-2="fourth | ****" level-3="second | **"/> <!-- bottom and middle are switching because of 'rank' -->
<X id="bottom3" id-parent="middle" text="fifth" text2="*****" status="1" rank="6" status2="any-value-here-make-its-status-not_null"/> <!--nothing happens with this line because of 'status' and 'status2' attributes -->
</A>
你的措辞掩盖了问题。您需要折叠层次结构(用 @id
和 @id-parent
属性表示),然后按 @rank
属性对路径进行排序。
此 XSLT 1.0 样式表(带有 EXSLT 扩展名):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common">
<xsl:key name="X-By-id" match="X" use="@id"/>
<xsl:key name="X-By-parent-id" match="X" use="string(@id-parent)"/>
<xsl:variable name="fold-rtf">
<xsl:apply-templates select="/" mode="fold"/>
</xsl:variable>
<xsl:variable name="folded-tree" select="exslt:node-set($fold-rtf)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="X[@status=0][not(@status2)]/@*[last()]">
<xsl:variable name="current" select=".."/>
<xsl:copy/>
<xsl:for-each select="$folded-tree">
<xsl:for-each select="key('X-By-id',$current/@id)">
<xsl:attribute name="chain-text">
<xsl:for-each select="ancestor-or-self::*">
<xsl:sort select="@rank"/>
<xsl:if test="position()!=1"> | </xsl:if>
<xsl:value-of select="concat(@text,' ',@text2)"/>
</xsl:for-each>
</xsl:attribute>
<xsl:for-each select="ancestor-or-self::*">
<xsl:sort select="@rank"/>
<xsl:attribute name="level-{position()}">
<xsl:value-of select="concat(@text,' | ',@text2)"/>
</xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="/|*" mode="fold">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="key('X-By-parent-id',string(@id))" mode="fold">
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出:
<A>
<X id="top" text="first" text2="*" status="0" rank="1"
chain-text="first *" level-1="first | *" />
<X id="middle" id-parent="top" text="second" text2="**" status="0" rank="3"
chain-text="first * | second **" level-1="first | *" level-2="second | **" />
<X id="bottom" id-parent="middle" text="third" text2="***" status="0" rank="6"
chain-text="first * | second ** | third ***" level-1="first | *" level-2="second | **" level-3="third | ***" />
<X id="bottom2" id-parent="middle" text="fourth" text2="****" status="0" rank="2"
chain-text="first * | fourth **** | second **" level-1="first | *" level-2="fourth | ****" level-3="second | **" /> <!--note rank! bottom and middle should be switching because "bottom2 has a "higher" rank-->
<X id="bottom3" id-parent="middle" text="fifth" text2="*****" status="2" rank="6" status2="any-value-make-its-status-not_null" />
</A>
请注意: 使用 node-set()
函数进行两步转换,使用 key()
函数对另一个输入文档进行上下文更改,强制对键进行字符串转换,因为有关于节点集作为键的特殊规则,也因为我想使用空字符串键...
建议:所有这些都可以翻译成几行XSLT 2.0+
现有的 XSLT 代码需要根据以下条件进行增强。
1 - 与递归算法一起添加检查每个比较行(元素)是否具有:
- 属性状态=0
- 并且属性 status2 为 null(这里意味着元素中没有属性 status2,实际上) (只有满足这两个条件才能创建链)
2 - 排名顺序。所有恢复(或恢复)的链最终都必须按 'rank' 属性排序 (它可能与本机递归匹配,但有时不匹配。无论如何 'rank' 属性具有更高的优先级。 Rank 没有固定的命名法(如 1-2-3-4),但它的链可以通过比较 'more'-'less' 来计算。 (1 < 3 < 7)。
源代码
<A>
<X id="top" text="first" text2="*" status="0" rank="1"/>
<X id="middle" id-parent="top" text="second" text2="**" status="0" rank="3"/>
<X id="bottom" id-parent="middle" text="third" text2="***" status="0" rank="6"/>
<X id="bottom2" id-parent="middle" text="fourth" text2="****" status="0" rank="2"/> <!--note rank! bottom and middle should be switching because "bottom2 has a "higher" rank-->
<X id="bottom3" id-parent="middle" text="fifth" text2="*****" status="2" rank="6" status2="any-value-make-its-status-not_null"/>
</A>
XSLT 转换
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="ref" match="X" use="@id"/>
<xsl:template match="X">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:variable name="Xs">
<xsl:call-template name="XATT">
<xsl:with-param name="currentX" select="."/>
<xsl:with-param name="X" select=".">
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$Xs[node()]">
<xsl:attribute name="chain-text">
<xsl:for-each select="$Xs/X">
<xsl:if test="position() ne 1"><xsl:text> | </xsl:text></xsl:if>
<xsl:value-of select="concat(@text, ' ', @text2)"/>
</xsl:for-each>
</xsl:attribute>
<xsl:for-each select="$Xs/X">
<xsl:attribute name="level-{position()}"><xsl:value-of select="concat(@text, ' | ', @text2)"/></xsl:attribute>
</xsl:for-each>
</xsl:if>
</xsl:copy>
</xsl:template>
<xsl:template name="XATT">
<xsl:param name="currentX"/>
<xsl:param name="X"/>
<xsl:choose>
<xsl:when test="$currentX/@id-parent">
<xsl:call-template name="XATT">
<xsl:with-param name="currentX" select="key('ref', $currentX/@id-parent)"/>
<xsl:with-param name="X">
<xsl:copy-of select="key('ref', $currentX/@id-parent)"/>
<xsl:copy-of select="$X"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$X"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94AcskR
互联scheme
期望输出
<?xml version="1.0" encoding="UTF-8"?><A>
<X id="top" text="first" text2="*" chain-text="first *" level-1="first | *"/>
<X id="middle" id-parent="top" text="second" text2="**" chain-text="first * | second **" level-1="first | *" level-2="second | **"/>
<X id="bottom" id-parent="middle" text="third" text2="***" chain-text="first * | second ** | third ***" level-1="first | *" level-2="second | **" level-3="third | ***"/>
<!--new conditions are clearly visible on the following lines-->
<X id="bottom2" id-parent="middle" text="fourth" text2="****" chain-text="first * | fourth **** | second **" level-1="first | *" level-2="fourth | ****" level-3="second | **"/> <!-- bottom and middle are switching because of 'rank' -->
<X id="bottom3" id-parent="middle" text="fifth" text2="*****" status="1" rank="6" status2="any-value-here-make-its-status-not_null"/> <!--nothing happens with this line because of 'status' and 'status2' attributes -->
</A>
你的措辞掩盖了问题。您需要折叠层次结构(用 @id
和 @id-parent
属性表示),然后按 @rank
属性对路径进行排序。
此 XSLT 1.0 样式表(带有 EXSLT 扩展名):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common">
<xsl:key name="X-By-id" match="X" use="@id"/>
<xsl:key name="X-By-parent-id" match="X" use="string(@id-parent)"/>
<xsl:variable name="fold-rtf">
<xsl:apply-templates select="/" mode="fold"/>
</xsl:variable>
<xsl:variable name="folded-tree" select="exslt:node-set($fold-rtf)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="X[@status=0][not(@status2)]/@*[last()]">
<xsl:variable name="current" select=".."/>
<xsl:copy/>
<xsl:for-each select="$folded-tree">
<xsl:for-each select="key('X-By-id',$current/@id)">
<xsl:attribute name="chain-text">
<xsl:for-each select="ancestor-or-self::*">
<xsl:sort select="@rank"/>
<xsl:if test="position()!=1"> | </xsl:if>
<xsl:value-of select="concat(@text,' ',@text2)"/>
</xsl:for-each>
</xsl:attribute>
<xsl:for-each select="ancestor-or-self::*">
<xsl:sort select="@rank"/>
<xsl:attribute name="level-{position()}">
<xsl:value-of select="concat(@text,' | ',@text2)"/>
</xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="/|*" mode="fold">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="key('X-By-parent-id',string(@id))" mode="fold">
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出:
<A>
<X id="top" text="first" text2="*" status="0" rank="1"
chain-text="first *" level-1="first | *" />
<X id="middle" id-parent="top" text="second" text2="**" status="0" rank="3"
chain-text="first * | second **" level-1="first | *" level-2="second | **" />
<X id="bottom" id-parent="middle" text="third" text2="***" status="0" rank="6"
chain-text="first * | second ** | third ***" level-1="first | *" level-2="second | **" level-3="third | ***" />
<X id="bottom2" id-parent="middle" text="fourth" text2="****" status="0" rank="2"
chain-text="first * | fourth **** | second **" level-1="first | *" level-2="fourth | ****" level-3="second | **" /> <!--note rank! bottom and middle should be switching because "bottom2 has a "higher" rank-->
<X id="bottom3" id-parent="middle" text="fifth" text2="*****" status="2" rank="6" status2="any-value-make-its-status-not_null" />
</A>
请注意: 使用 node-set()
函数进行两步转换,使用 key()
函数对另一个输入文档进行上下文更改,强制对键进行字符串转换,因为有关于节点集作为键的特殊规则,也因为我想使用空字符串键...
建议:所有这些都可以翻译成几行XSLT 2.0+