【问题标题】:Recursion: integrating complex condition and order by rank递归:按等级整合复杂条件和顺序
【发布时间】:2020-01-22 10:04:09
【问题描述】:

现有的 XSLT 代码需要通过以下条件来增强。

1 - 与递归算法一起添加检查每个比较行(元素)是否具有:

  • 属性状态=0
  • 且属性status2为null(这里表示元素内实际上没有status2属性) (只有满足这两个条件才能创建链)

2 - 排名顺序。所有恢复(或恢复)的链最终必须按 'rank' 属性排序 (它可能与本机递归匹配,但有时不匹配。无论如何,'rank' 属性具有更高的优先级。 Rank 没有稳定的命名法(如 1-2-3-4),但它的链可以通过比较“更多”-“更少”来计算。 (1

源代码

<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>

【问题讨论】:

    标签: xml xslt xslt-1.0


    【解决方案1】:

    您的措辞掩盖了问题。您需要折叠层次结构(用@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+

    【讨论】:

    • 感谢代码和建议!我会仔细检查两者。
    • stackoverflow.com/questions/59921582/… 为您的代码添加了另一种模式。它删除所有不形成递归链的节点。但是有一个关于优化的问题。也许你会对这件事有自己的看法。在我看来,其中一种选择是在没有 DELETE 模式的情况下解决问题,并且最初只输出形成链的节点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 2019-06-25
    • 2021-04-02
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    相关资源
    最近更新 更多