Le code XSLT existant doit être amélioré par les conditions suivantes.

1 - Avec l'algorithme de récursivité, ajoutez la vérification que chaque ligne de comparaison (élément) a :

  • l'attribut status=0
  • et l'attribut status2 est nul (ici cela signifie qu'il n'y a pas d'attribut status2 dans l'élément, en fait) (une chaîne ne peut être créée que si ces deux conditions sont vraies)

2 - Ordre de classement. Toutes les chaînes restaurées (ou restaurées) doivent finalement être classées par l'attribut 'rank' (il peut correspondre à une récursivité native, mais parfois pas. De toute façon, l'attribut 'rank' a une priorité plus élevée. Rank n'a pas de nomenclature stable (comme 1-2- 3-4) mais sa chaîne peut être calculée en comparant 'plus'-'moins' (1 < 3 < 7).

code source

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

Transformation 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

schéma d'interconnexion

sortie souhaitée

<?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>
0
Alex 22 janv. 2020 à 13:04

1 réponse

Meilleure réponse

Votre formulation masque le problème. Vous devez plier la hiérarchie (exprimée avec les attributs @id et @id-parent), puis trier le chemin par l'attribut @rank.

Cette feuille de style XSLT 1.0 (avec les extensions 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>

Les sorties:

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

A noter : l'utilisation de la fonction node-set() pour une transformation en deux étapes, le changement de contexte pour l'utilisation de la fonction key() avec un autre document d'entrée, forçant le transtypage de la clé car il existe des règles spéciales concernant les ensembles de nœuds en tant que clés et aussi parce que je voulais utiliser une clé de chaîne vide ...

Conseil : tout cela pourrait se traduire en quelques lignes de XSLT 2.0+

1
Alejandro 22 janv. 2020 à 22:57