XSLT 1.0 - Grouper par liste de noeuds identiques

XSLT 1.0 - Grouper par liste de noeuds identiques - XML/XSL - Programmation

Marsh Posté le 29-05-2019 à 16:38:01    

Bonjour,
 
J'ai un fichier XML ayant la structure suivante :

<order>
    <version>
        <version_name>ABO</version_name>
        <sequence>
            <sequence_name>ABOPAL</sequence_name>
            <hopper number="0" id="11">CV</hopper>
            <hopper number="1" id="12">INT</hopper>
            <hopper number="2" id="13">ENC</hopper>
        </sequence>
        <sequence>
            <sequence_name>ABOCAR</sequence_name>
            <hopper number="0" id="11">CV</hopper>
            <hopper number="1" id="12">INT</hopper>
            <hopper number="2" id="13">ENC</hopper>
        </sequence>
    </version>
    <version>
        <version_name>GEN</version_name>
        <sequence>
            <sequence_name>GENPAL</sequence_name>
            <hopper number="0" id="11">CV</hopper>
            <hopper number="1" id="12">INT</hopper>
            <hopper number="2" id="13">ENC</hopper>
        </sequence>
        <sequence>
            <sequence_name>ABOCAR</sequence_name>
            <hopper number="0" id="11">CV</hopper>
            <hopper number="1" id="12">INT</hopper>
            <hopper number="2" id=""></hopper>
        </sequence>
    </version>
</order>


 
Dans mon order, j'ai donc 2 versions, et dans chaque version, j'ai 2 séquences, contenant des hoppers numérotés associés ou non à des id.
 
Dans l'exemple ci-dessus, les 3 premières séquences sont identiques (mêmes id en face de chaque numéro de hopper).
 
Je souhaite grouper tout cela par séquence identique (même combinaison de hoppers), pour obtenir le résultat suivant :
 

<order>
    <sequence>
        <version>ABO</version>
        <version>GEN</version>
        <sequence_name>ABOPAL</sequence_name>
        <sequence_name>ABOCAR</sequence_name>
        <sequence_name>GENPAL</sequence_name>
        <hopper number="0" id="11">CV</hopper>
        <hopper number="1" id="12">INT</hopper>
        <hopper number="2" id="13">ENC</hopper>
    </sequence>
    <sequence>
        <version>GEN</version>
        <sequence_name>GENCAR</sequence_name>
        <hopper number="0" id="11">CV</hopper>
        <hopper number="1" id="12">INT</hopper>
        <hopper number="2" id=""></hopper>
    </sequence>
</order>


 
J'ai trouvé des infos sur la page http://www.jenitennison.com/xslt/g [...] nchian.xml pour grouper à l'aide d'une clé correspondant à un champ du XML,
mais dans mon cas, la clé devrait être une liste de noeuds (les hoppers).
 
Quelqu'un a-t'il une idée sur la façon de procéder pour y arriver ?

Reply

Marsh Posté le 29-05-2019 à 16:38:01   

Reply

Marsh Posté le 30-05-2019 à 10:13:52    

Si je pige bien, c'est la liste des hopper d'une sequence qui est ta clé.
 

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet  
  3.  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.  exclude-result-prefixes="#all"
  6.  version="3.0">
  7.  
  8.  <xsl:output indent="true"/>
  9.  
  10.  <xsl:template match="/">
  11.    <xsl:apply-templates/>
  12.  </xsl:template>
  13.  
  14.  <xsl:template match="order">
  15.    <order>
  16.      <xsl:for-each-group select="version/sequence" group-by="string-join(tail(*)!string(), '-')">
  17.        <xsl:variable name ="versions" as="xs:string*">
  18.          <xsl:for-each select="current-group()">
  19.            <xsl:sequence select="preceding-sibling::version_name/data()"/>
  20.          </xsl:for-each>
  21.        </xsl:variable>
  22.        <xsl:variable name ="sequence_names" as="xs:string*">
  23.          <xsl:for-each select="current-group()">
  24.            <xsl:sequence select="sequence_name/data()"/>
  25.          </xsl:for-each>
  26.        </xsl:variable>
  27.        <sequence>
  28.          <xsl:for-each select="distinct-values($versions)">
  29.            <version><xsl:value-of select="current()"/></version>
  30.          </xsl:for-each>
  31.          <xsl:for-each select="distinct-values($sequence_names)">
  32.            <sequence_name><xsl:value-of select="current()"/></sequence_name>
  33.          </xsl:for-each>
  34.          <xsl:copy-of select="head(current-group())/hopper"/>
  35.        </sequence>
  36.      </xsl:for-each-group>
  37.    </order>
  38.  </xsl:template>
  39.  
  40. </xsl:stylesheet>


 
Sur tes données, ça donne en sortie

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <order>
  3.   <sequence>
  4.      <version>ABO</version>
  5.      <version>GEN</version>
  6.      <sequence_name>ABOPAL</sequence_name>
  7.      <sequence_name>ABOCAR</sequence_name>
  8.      <sequence_name>GENPAL</sequence_name>
  9.      <hopper number="0" id="11">CV</hopper>
  10.      <hopper number="1" id="12">INT</hopper>
  11.      <hopper number="2" id="13">ENC</hopper>
  12.   </sequence>
  13.   <sequence>
  14.      <version>GEN</version>
  15.      <sequence_name>ABOCAR</sequence_name>
  16.      <hopper number="0" id="11">CV</hopper>
  17.      <hopper number="1" id="12">INT</hopper>
  18.      <hopper number="2" id=""/>
  19.   </sequence>
  20. </order>


 
J'ai mis version="3.0" mais ça marche aussi en xsl 2.0
J'ai regroupé les éléments sequence sur une clé composite obtenue en concaténant les contenus des hoppers et en séparant avec des - ce qui fait une bonne clé de tri des sequences :
string-join(tail(*)!string(), '-') ça construit la clé ainsi : tail(*) pour virer le premier fils de sequence, qui n'est pas un hopper, ça me fait la liste des hoppers, tail(*)!string() ça me fait la liste de leurs contenus, et le reste est explicite.
 
Et si tu veux trier en sortie les versions et/ou sequence_name, tu remplace le/les <xsl:for-each select="distinct-values(...)"> par <xsl:for-each select="sort(distinct-values(...))">
 
Bon, si on veut faire ça en xslt 1.0 c'est plus coton (programmer en xslt 1.0 de nos jours, c'est comme utiliser windows 95 comme OS, bref c'est obsolète et complètement dépassé). Tu as pas d'autre possibilité? parce que ça, avec la version Home Edition (gratuite) de Saxon, ça marche.
 
Sinon, il faudra que tu fasses du multi-passe avec des modes distincts, et transformation de ton arbre.
Bref, tu transformes tes données initiales avec la clé composite explicitement ajoutée à la main (technique à la Schwartzian transform du Perl)

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <order>
  3.    <sequence>
  4.      <key>CV-INT-ENC</key>
  5.      <version_name>ABO</version_name>
  6.      <sequence_name>ABOPAL</sequence_name>
  7.      <hopper number="0" id="11">CV</hopper>
  8.      <hopper number="1" id="12">INT</hopper>
  9.      <hopper number="2" id="13">ENC</hopper>
  10.    </sequence>
  11.    <sequence>
  12.      <key>CV-INT-ENC</key>
  13.      <version_name>ABO</version_name>
  14.      <sequence_name>ABOCAR</sequence_name>
  15.      <hopper number="0" id="11">CV</hopper>
  16.      <hopper number="1" id="12">INT</hopper>
  17.      <hopper number="2" id="13">ENC</hopper>
  18.    </sequence>
  19.    <sequence>
  20.      <key>CV-INT-ENC</key>
  21.      <version_name>GEN</version_name>
  22.      <sequence_name>GENPAL</sequence_name>
  23.      <hopper number="0" id="11">CV</hopper>
  24.      <hopper number="1" id="12">INT</hopper>
  25.      <hopper number="2" id="13">ENC</hopper>
  26.    </sequence>
  27.    <sequence>
  28.      <key>CV-INT-</key>
  29.      <version_name>GEN</version_name>
  30.      <sequence_name>ABOCAR</sequence_name>
  31.      <hopper number="0" id="11">CV</hopper>
  32.      <hopper number="1" id="12">INT</hopper>
  33.      <hopper number="2" id=""/>
  34.    </sequence>
  35. </order>


et une seconde passe pour générer tes données regroupées par clé (mais ça va être encore du boulot pour ne pas avoir des entrées version ou sequence_name dupliquées, il faudra peut être trois passes. Bon, de toute façon, je touche plus au xslt 1.0, faut être maso pour en faire quand on voit tout ce qui a été apporté par les versions 2.0 et 3.0 et qu'il y a un outil gratuit qui le fait pour un usage perso).
 
A+,


Message édité par gilou le 30-05-2019 à 12:59:40

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 31-05-2019 à 10:35:30    

Merci pour les exemples et explications.
C'est pour un usage pro avec un logiciel qui utilise PrintXML 8.1, donc je suis bloqué en version 1.0, et je ne peux par corriger le XML (même à la main, car c'est pour un usage régulier d'impression en PDF).
Quand tu parles de multi-passes, c'est possible avec un seul fichier XSL, ou il faut générer des fichiers XML intermédiaires ? (Dans ce cas, ça me paraît difficile avec PrintXML)

Reply

Marsh Posté le 01-06-2019 à 14:08:44    

fred_53 a écrit :

Merci pour les exemples et explications.
C'est pour un usage pro avec un logiciel qui utilise PrintXML 8.1, donc je suis bloqué en version 1.0, et je ne peux par corriger le XML (même à la main, car c'est pour un usage régulier d'impression en PDF).
Quand tu parles de multi-passes, c'est possible avec un seul fichier XSL, ou il faut générer des fichiers XML intermédiaires ? (Dans ce cas, ça me paraît difficile avec PrintXML)

Tu parles de PrinceXML je suppose. PrinceXML il imprime du XML, donc ce qui a généré le XML, il s'en fiche à priori.
Mais manifestement, tu as un outil obsolète en amont (surtout que PrinceXML, il est en 12.5 actuellement. Je ne sais pas quel est ton outil Pro, mais ça fait pas sérieux, ça).
Rien ne vaut un exemple pour expliciter la technique:

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet  
  3.  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.  version="1.0">
  6.  
  7.  <xsl:output indent="true"/>
  8.  
  9.  <xsl:template match="/">
  10.    <xsl:variable name="pass1">
  11.      <xsl:apply-templates select="." mode="toto"/>
  12.    </xsl:variable>
  13.    <xsl:apply-templates select="$pass1" mode="titi"/>
  14.  </xsl:template>  
  15.  
  16.  <xsl:template match="*" mode="toto">
  17.    <xsl:element name="{concat('TOTO-', local-name())}">
  18.      <xsl:apply-templates select="@*|node()" mode="toto"/>
  19.    </xsl:element>
  20.  </xsl:template>
  21.  
  22.  <xsl:template match="@*" mode="toto">
  23.    <xsl:copy/>
  24.  </xsl:template>
  25.  
  26.  <xsl:template match="*" mode="titi">
  27.    <xsl:copy>
  28.      <xsl:apply-templates select="@*|node()" mode="titi"/>
  29.    </xsl:copy>
  30.  </xsl:template>
  31.  
  32.  <xsl:template match="@*" mode="titi">
  33.    <xsl:attribute name="{concat(local-name(), '-TITI')}">
  34.      <xsl:copy/>
  35.    </xsl:attribute>
  36.  </xsl:template>
  37.  
  38. </xsl:stylesheet>


Le premier mode, toto, va rajouter TOTO- a tous les noms d'éléments.
Le second mode, titi,  va rajouter -TITI à tous les noms d'attributs.
On applique le premier mode aux données initiales, et on stocke le résultat dans une variable, ici, pass1.
On applique le second mode au résultat de la transformation précédente, ie pass1.
Et au final on a le résultat des deux transformations successives.
 
Après, au prix d'un peu de mémoire et de temps d'exécution, on peut factoriser pour rendre le traitement des passes homogène :

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet  
  3.  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.  version="1.0">
  6.  
  7.  <xsl:output indent="true"/>
  8.  
  9.  <xsl:template match="/">
  10.    <xsl:variable name="pass1">
  11.      <xsl:apply-templates select="." mode="toto"/>
  12.    </xsl:variable>
  13.    <xsl:variable name="pass2">
  14.      <xsl:apply-templates select="$pass1" mode="titi"/>
  15.    </xsl:variable>
  16.    <xsl:apply-templates select="$pass2" mode="identity"/>
  17.  </xsl:template>  
  18.  
  19.  <xsl:template match="*" mode="toto">
  20.    <xsl:element name="{concat('TOTO-', local-name())}">
  21.      <xsl:apply-templates select="@*|node()" mode="toto"/>
  22.    </xsl:element>
  23.  </xsl:template>
  24.  
  25.  <xsl:template match="@*" mode="toto">
  26.    <xsl:copy/>
  27.  </xsl:template>
  28.  
  29.  <xsl:template match="*" mode="titi">
  30.    <xsl:copy>
  31.      <xsl:apply-templates select="@*|node()" mode="titi"/>
  32.    </xsl:copy>
  33.  </xsl:template>
  34.  
  35.  <xsl:template match="@*" mode="titi">
  36.    <xsl:attribute name="{concat(local-name(), '-TITI')}">
  37.      <xsl:copy/>
  38.    </xsl:attribute>
  39.  </xsl:template>
  40.  
  41.  <xsl:template match="/|@*|*" mode="identity">
  42.    <xsl:copy>
  43.      <xsl:apply-templates select="@*|node()" mode="identity"/>
  44.    </xsl:copy>
  45.  </xsl:template>
  46.  
  47. </xsl:stylesheet>


A+,


Message édité par gilou le 03-06-2019 à 12:31:18

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 01-06-2019 à 17:09:56    

Et au final tu obtiens un truc de ce genre:
 

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet  
  3.  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.  version="1.0">
  6.  
  7.  <xsl:output indent="true"/>
  8.  <xsl:strip-space elements="*"/>
  9.  
  10.  <xsl:template match="/">
  11.    <xsl:variable name="pass1">
  12.      <xsl:apply-templates select="." mode="pass1"/>
  13.    </xsl:variable>
  14.    <xsl:variable name="pass2">
  15.      <xsl:apply-templates select="$pass1" mode="pass2"/>
  16.    </xsl:variable>
  17.    <xsl:variable name="pass3">
  18.      <xsl:apply-templates select="$pass2" mode="pass3"/>
  19.    </xsl:variable>
  20.    <xsl:variable name="pass4">
  21.      <xsl:apply-templates select="$pass3" mode="pass4"/>
  22.    </xsl:variable>
  23.    <xsl:apply-templates select="$pass4" mode="pass5"/>
  24.  </xsl:template>  
  25.  
  26.  <!-- copie par défaut -->
  27.  <xsl:template match="/|@*|*" mode="pass1">
  28.    <xsl:copy>
  29.      <xsl:apply-templates select="@*|node()" mode="pass1"/>
  30.    </xsl:copy>
  31.  </xsl:template>
  32.  
  33.  <xsl:template match="/|@*|*" mode="pass2">
  34.    <xsl:copy>
  35.      <xsl:apply-templates select="@*|node()" mode="pass2"/>
  36.    </xsl:copy>
  37.  </xsl:template>
  38.  
  39.  <xsl:template match="/|@*|*" mode="pass3">
  40.    <xsl:copy>
  41.      <xsl:apply-templates select="@*|node()" mode="pass3"/>
  42.    </xsl:copy>
  43.  </xsl:template>
  44.  
  45.  <xsl:template match="/|@*|*" mode="pass4">
  46.    <xsl:copy>
  47.      <xsl:apply-templates select="@*|node()" mode="pass4"/>
  48.    </xsl:copy>
  49.  </xsl:template>
  50.  
  51.  <xsl:template match="/|@*|*" mode="pass5">
  52.    <xsl:copy>
  53.      <xsl:apply-templates select="@*|node()" mode="pass5"/>
  54.    </xsl:copy>
  55.  </xsl:template>
  56.  
  57.  <!-- =================================================== -->
  58.  
  59.  <xsl:template match="version" mode="pass1">
  60.    <xsl:apply-templates select="*|@*" mode="pass1"/>
  61.  </xsl:template>
  62.  
  63.  <xsl:template match="version_name" mode="pass1"/>
  64.  
  65.  <xsl:template match="sequence" mode="pass1">
  66.    <xsl:copy>
  67.      <xsl:apply-templates select="@*" mode="pass1"/>
  68.      <xsl:variable name="key">
  69.        <xsl:for-each select="hopper">
  70.          <xsl:value-of select="current()"/>
  71.          <xsl:value-of select="'~'"/>
  72.        </xsl:for-each>
  73.      </xsl:variable>
  74.      <xsl:element name="sort-key">
  75.        <xsl:value-of select="$key"/>
  76.      </xsl:element>  
  77.      <xsl:copy-of select="preceding-sibling::version_name[1]"/>
  78.      <xsl:apply-templates select="node()" mode="pass1"/>
  79.    </xsl:copy>
  80.  </xsl:template>
  81.  
  82.  <!-- =================================================== -->
  83.  
  84.  <xsl:template match="order" mode="pass2">
  85.    <xsl:copy>
  86.      <xsl:apply-templates select="sequence" mode="pass2">
  87.        <xsl:sort select="sort-key"/>
  88.      </xsl:apply-templates>
  89.    </xsl:copy>
  90.  </xsl:template>
  91.  
  92.  <xsl:template match="version_name" mode="pass2">
  93.    <xsl:element name="version">
  94.      <xsl:apply-templates select="@*|node()" mode="pass2"/>
  95.    </xsl:element>
  96.  </xsl:template>
  97.  
  98.  <!-- =================================================== -->
  99.  <xsl:template match="sequence[string(sort-key) != string(preceding-sibling::sequence[1]/sort-key)]" mode="pass3">
  100.    <xsl:copy>
  101.      <xsl:apply-templates select="@*|node()" mode="pass3"/>
  102.      <xsl:apply-templates select="following-sibling::sequence[string(sort-key) = string(current()/sort-key)]" mode="pass3.1"/>
  103.    </xsl:copy>
  104.  </xsl:template>
  105.  
  106.  <xsl:template match="sequence[string(sort-key) = string(preceding-sibling::sequence[1]/sort-key)]" mode="pass3"/>
  107.  
  108.  <xsl:template match="sequence" mode="pass3.1">
  109.    <xsl:apply-templates select="version" mode="pass3"/>
  110.    <xsl:apply-templates select="sequence_name" mode="pass3"/>
  111.  </xsl:template>
  112.  
  113.  <xsl:template match="sort-key" mode="pass3"/>
  114.  <!-- =================================================== -->
  115.  
  116.  <xsl:template match="sequence" mode="pass4">
  117.    <xsl:copy>
  118.      <xsl:apply-templates select="version" mode="pass4">
  119.        <xsl:sort select="string(.)"/>
  120.      </xsl:apply-templates>
  121.      <xsl:apply-templates select="sequence_name" mode="pass4">
  122.        <xsl:sort select="string(.)"/>
  123.      </xsl:apply-templates>
  124.      <xsl:apply-templates select="hopper" mode="pass4"/>
  125.    </xsl:copy>
  126.  </xsl:template>
  127.  
  128.  <!-- =================================================== -->
  129.  
  130.  <xsl:template match="version[string()=string(preceding-sibling::version[1])]" mode="pass5"/>
  131.    
  132.  <xsl:template match="sequence_name[string()=string(preceding-sibling::sequence_name[1])]" mode="pass5"/>
  133.  
  134. </xsl:stylesheet>


 
qui fait le taff (testé en live sur tes données avec Oxygen).
 
L'étape 1 calcule la clé identifiant les suite de hopper identiques et l'ajoute comme élément sort-key à chaque séquence
L'étape 2 trie les sequences par sort-key
L'étape 3 traite les sequences de même sort-keys (donc successifs) en différenciant le traitement du premier de la suite de celui des suivants
L'étape 4 ordonne les version et sequence_name d'une même sequence pour pouvoir virer les doublons à l'étape 5.
 
Mais c'est bien lourd comparé à du xsl moderne qui fait ça en une seule template et le mode par défaut:

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet  
  3.  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.  exclude-result-prefixes="#all"
  6.  version="3.0">
  7.  
  8.  <xsl:output indent="true"/>
  9.  
  10.  <xsl:template match="/">
  11.    <order>
  12.      <xsl:for-each-group select="order/version/sequence" group-by="string-join(tail(*)!string(), '~')">
  13.        <xsl:variable name ="versions" as="xs:string*">
  14.          <xsl:for-each select="current-group()">
  15.            <xsl:sequence select="preceding-sibling::version_name/data()"/>
  16.          </xsl:for-each>
  17.        </xsl:variable>
  18.        <xsl:variable name ="sequence_names" as="xs:string*">
  19.          <xsl:for-each select="current-group()">
  20.            <xsl:sequence select="sequence_name/data()"/>
  21.          </xsl:for-each>
  22.        </xsl:variable>
  23.        <sequence>
  24.          <xsl:for-each select="distinct-values($versions)">
  25.            <version><xsl:value-of select="current()"/></version>
  26.          </xsl:for-each>
  27.          <xsl:for-each select="distinct-values($sequence_names)">
  28.            <sequence_name><xsl:value-of select="current()"/></sequence_name>
  29.          </xsl:for-each>
  30.          <xsl:copy-of select="head(current-group())/hopper"/>
  31.        </sequence>
  32.      </xsl:for-each-group>
  33.    </order>
  34.  </xsl:template>
  35.  
  36. </xsl:stylesheet>


Note finale, si je choisis ~ comme séparateur des constituants des clés de tri, c'est parce que sa valeur ascii est inférieure à celle des chiffres et lettres, et que ça convient donc mieux pour ne pas perturber un tri lexicographique.
 
A+,


Message édité par gilou le 01-06-2019 à 17:50:40

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 04-06-2019 à 10:38:04    

Oui il s'agit bien de PrinceXML.
 
J'ai testé ton code dans Oxygen XML Editor 10.3 et dans Notepad++ 7.6.3/XML Tools 2.4.11.0, et j'ai obtenu respectivement les erreurs suivantes :
 

E: [Saxon6.5.5] indent must be yes or no or an integer


Unable to apply  transformation on current source. Make sure that XSL is valid.


 
Si je remplace true par yes dans <xsl:output indent="yes"/>,  j'ai alors les erreurs suivantes :

E [Saxon6.5.5] Cannot process a result tree fragment as a node-set under XSLT 1.0


The transformation has generated empty document.


 
Est-ce du à ma version d'Oxygen et de Notepad++ ?

Reply

Marsh Posté le 04-06-2019 à 15:02:40    

La version de Saxon utilisée dans Oxygen (qui est en version 21 de nos jours, tandis que Saxon est en 9.9...).
Il faut transformer les paramètres passés a chaque passe (des arbres résultant de la transfo d'une passe) en node-set. En xsl 2.0 avec la notion de sequence, ce problème n'existe plus.
 
Avec ce code, ça marche (on peut encore poser saxon6.5.5 comme processeur xsl dans Oxygen 20):  

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet 
  3.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.   xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.   xmlns:exsl="http://exslt.org/common"
  6.   version="1.0">
  7.  
  8.   <xsl:output indent="yes"/>
  9.   <xsl:strip-space elements="*"/>
  10.  
  11.   <xsl:template match="/">
  12.     <xsl:variable name="pass1">
  13.       <xsl:apply-templates select="." mode="pass1"/>
  14.     </xsl:variable>
  15.     <xsl:variable name="pass2">
  16.       <xsl:apply-templates select="exsl:node-set($pass1)" mode="pass2"/>
  17.     </xsl:variable>
  18.     <xsl:variable name="pass3">
  19.       <xsl:apply-templates select="exsl:node-set($pass2)" mode="pass3"/>
  20.     </xsl:variable>
  21.     <xsl:variable name="pass4">
  22.       <xsl:apply-templates select="exsl:node-set($pass3)" mode="pass4"/>
  23.     </xsl:variable>
  24.     <xsl:apply-templates select="exsl:node-set($pass4)" mode="pass5"/>
  25.   </xsl:template
  26.  
  27.   <!-- copie par défaut -->
  28.   <xsl:template match="/|@*|*" mode="pass1">
  29.     <xsl:copy>
  30.       <xsl:apply-templates select="@*|node()" mode="pass1"/>
  31.     </xsl:copy>
  32.   </xsl:template>
  33.  
  34.   <xsl:template match="/|@*|*" mode="pass2">
  35.     <xsl:copy>
  36.       <xsl:apply-templates select="@*|node()" mode="pass2"/>
  37.     </xsl:copy>
  38.   </xsl:template>
  39.  
  40.   <xsl:template match="/|@*|*" mode="pass3">
  41.     <xsl:copy>
  42.       <xsl:apply-templates select="@*|node()" mode="pass3"/>
  43.     </xsl:copy>
  44.   </xsl:template>
  45.  
  46.   <xsl:template match="/|@*|*" mode="pass4">
  47.     <xsl:copy>
  48.       <xsl:apply-templates select="@*|node()" mode="pass4"/>
  49.     </xsl:copy>
  50.   </xsl:template>
  51.  
  52.   <xsl:template match="/|@*|*" mode="pass5">
  53.     <xsl:copy>
  54.       <xsl:apply-templates select="@*|node()" mode="pass5"/>
  55.     </xsl:copy>
  56.   </xsl:template>
  57.  
  58.   <!-- =================================================== -->
  59.  
  60.   <xsl:template match="version" mode="pass1">
  61.     <xsl:apply-templates select="*|@*" mode="pass1"/>
  62.   </xsl:template>
  63.  
  64.   <xsl:template match="version_name" mode="pass1"/>
  65.  
  66.   <xsl:template match="sequence" mode="pass1">
  67.     <xsl:copy>
  68.       <xsl:apply-templates select="@*" mode="pass1"/>
  69.       <xsl:variable name="key">
  70.         <xsl:for-each select="hopper">
  71.           <xsl:value-of select="current()"/>
  72.           <xsl:value-of select="'~'"/>
  73.         </xsl:for-each>
  74.       </xsl:variable>
  75.       <xsl:element name="sort-key">
  76.         <xsl:value-of select="$key"/>
  77.       </xsl:element> 
  78.       <xsl:copy-of select="preceding-sibling::version_name[1]"/>
  79.       <xsl:apply-templates select="node()" mode="pass1"/>
  80.     </xsl:copy>
  81.   </xsl:template>
  82.  
  83.   <!-- =================================================== -->
  84.  
  85.   <xsl:template match="order" mode="pass2">
  86.     <xsl:copy>
  87.       <xsl:apply-templates select="sequence" mode="pass2">
  88.         <xsl:sort select="sort-key"/>
  89.       </xsl:apply-templates>
  90.     </xsl:copy>
  91.   </xsl:template>
  92.  
  93.   <xsl:template match="version_name" mode="pass2">
  94.     <xsl:element name="version">
  95.       <xsl:apply-templates select="@*|node()" mode="pass2"/>
  96.     </xsl:element>
  97.   </xsl:template>
  98.  
  99.   <!-- =================================================== -->
  100.  
  101.   <xsl:template match="sequence[string(sort-key) != string(preceding-sibling::sequence[1]/sort-key)]" mode="pass3">
  102.     <xsl:copy>
  103.       <xsl:apply-templates select="@*|node()" mode="pass3"/>
  104.       <xsl:apply-templates select="following-sibling::sequence[string(sort-key) = string(current()/sort-key)]" mode="pass3.1"/>
  105.     </xsl:copy>
  106.   </xsl:template>
  107.  
  108.   <xsl:template match="sequence[string(sort-key) = string(preceding-sibling::sequence[1]/sort-key)]" mode="pass3"/>
  109.  
  110.   <xsl:template match="sequence" mode="pass3.1">
  111.     <xsl:apply-templates select="version" mode="pass3"/>
  112.     <xsl:apply-templates select="sequence_name" mode="pass3"/>
  113.   </xsl:template>
  114.  
  115.   <xsl:template match="sort-key" mode="pass3"/>
  116.  
  117.   <!-- =================================================== -->
  118.  
  119.   <xsl:template match="sequence" mode="pass4">
  120.     <xsl:copy>
  121.       <xsl:apply-templates select="version" mode="pass4">
  122.         <xsl:sort select="string(.)"/>
  123.       </xsl:apply-templates>
  124.       <xsl:apply-templates select="sequence_name" mode="pass4">
  125.         <xsl:sort select="string(.)"/>
  126.       </xsl:apply-templates>
  127.       <xsl:apply-templates select="hopper" mode="pass4"/>
  128.     </xsl:copy>
  129.   </xsl:template>
  130.  
  131.   <!-- =================================================== -->
  132.  
  133.   <xsl:template match="version[string()=string(preceding-sibling::version[1])]" mode="pass5"/>
  134.  
  135.   <xsl:template match="sequence_name[string()=string(preceding-sibling::sequence_name[1])]" mode="pass5"/>
  136.  
  137. </xsl:stylesheet>


A+,


Message édité par gilou le 04-06-2019 à 15:09:46

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 07-06-2019 à 17:27:20    

Merci beaucoup, cela fonctionne dans Oxygen.
 
Il faut maintenant que je regarde comme j'intègre cela dans mon fichier XSL, car il doit se présenter sous la forme suivante :
 

Code :
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:template name="myTemplate">
  4.         <xsl:for-each select="/order/sequence">
  5.             ...
  6.         </xsl:for-each>
  7.     </xsl:template>
  8. </xsl:stylesheet>

Reply

Marsh Posté le 07-06-2019 à 18:06:25    

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet 
  3.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.   xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.   xmlns:exsl="http://exslt.org/common"
  6.   version="1.0">
  7.  
  8.   <xsl:output indent="yes"/>
  9.   <xsl:strip-space elements="*"/>
  10.   <!-- Pour tester, car un template nommé doit être appellé
  11.   <xsl:template match="/">
  12.     <xsl:call-template name="myTemplate"/>
  13.   </xsl:template>
  14.   -->
  15.  
  16.   <xsl:template name="myTemplate">
  17.     <xsl:variable name="pass1">
  18.       <xsl:apply-templates select="/" mode="pass1"/>
  19.     </xsl:variable>
  20.     <xsl:variable name="pass2">
  21.       <xsl:apply-templates select="exsl:node-set($pass1)" mode="pass2"/>
  22.     </xsl:variable>
  23.     <xsl:variable name="pass3">
  24.       <xsl:apply-templates select="exsl:node-set($pass2)" mode="pass3"/>
  25.     </xsl:variable>
  26.     <xsl:variable name="pass4">
  27.       <xsl:apply-templates select="exsl:node-set($pass3)" mode="pass4"/>
  28.     </xsl:variable>
  29.     <xsl:apply-templates select="exsl:node-set($pass4)" mode="pass5"/>
  30.   </xsl:template
  31.  
  32.   <!-- copie par défaut -->
  33.   <xsl:template match="/|@*|*" mode="pass1">
  34.     <xsl:copy>
  35.       <xsl:apply-templates select="@*|node()" mode="pass1"/>
  36.     </xsl:copy>
  37.   </xsl:template>
  38.  
  39.   <xsl:template match="/|@*|*" mode="pass2">
  40.     <xsl:copy>
  41.       <xsl:apply-templates select="@*|node()" mode="pass2"/>
  42.     </xsl:copy>
  43.   </xsl:template>
  44.  
  45.   <xsl:template match="/|@*|*" mode="pass3">
  46.     <xsl:copy>
  47.       <xsl:apply-templates select="@*|node()" mode="pass3"/>
  48.     </xsl:copy>
  49.   </xsl:template>
  50.  
  51.   <xsl:template match="/|@*|*" mode="pass4">
  52.     <xsl:copy>
  53.       <xsl:apply-templates select="@*|node()" mode="pass4"/>
  54.     </xsl:copy>
  55.   </xsl:template>
  56.  
  57.   <xsl:template match="/|@*|*" mode="pass5">
  58.     <xsl:copy>
  59.       <xsl:apply-templates select="@*|node()" mode="pass5"/>
  60.     </xsl:copy>
  61.   </xsl:template>
  62.  
  63.   <!-- =================================================== -->
  64.  
  65.   <xsl:template match="version" mode="pass1">
  66.     <xsl:apply-templates select="*|@*" mode="pass1"/>
  67.   </xsl:template>
  68.  
  69.   <xsl:template match="version_name" mode="pass1"/>
  70.  
  71.   <xsl:template match="sequence" mode="pass1">
  72.     <xsl:copy>
  73.       <xsl:apply-templates select="@*" mode="pass1"/>
  74.       <xsl:variable name="key">
  75.         <xsl:for-each select="hopper">
  76.           <xsl:value-of select="current()"/>
  77.           <xsl:value-of select="'~'"/>
  78.         </xsl:for-each>
  79.       </xsl:variable>
  80.       <xsl:element name="sort-key">
  81.         <xsl:value-of select="$key"/>
  82.       </xsl:element> 
  83.       <xsl:copy-of select="preceding-sibling::version_name[1]"/>
  84.       <xsl:apply-templates select="node()" mode="pass1"/>
  85.     </xsl:copy>
  86.   </xsl:template>
  87.  
  88.   <!-- =================================================== -->
  89.  
  90.   <xsl:template match="order" mode="pass2">
  91.     <xsl:copy>
  92.       <xsl:apply-templates select="sequence" mode="pass2">
  93.         <xsl:sort select="sort-key"/>
  94.       </xsl:apply-templates>
  95.     </xsl:copy>
  96.   </xsl:template>
  97.  
  98.   <xsl:template match="version_name" mode="pass2">
  99.     <xsl:element name="version">
  100.       <xsl:apply-templates select="@*|node()" mode="pass2"/>
  101.     </xsl:element>
  102.   </xsl:template>
  103.  
  104.   <!-- =================================================== -->
  105.  
  106.   <xsl:template match="sequence[string(sort-key) != string(preceding-sibling::sequence[1]/sort-key)]" mode="pass3">
  107.     <xsl:copy>
  108.       <xsl:apply-templates select="@*|node()" mode="pass3"/>
  109.       <xsl:apply-templates select="following-sibling::sequence[string(sort-key) = string(current()/sort-key)]" mode="pass3.1"/>
  110.     </xsl:copy>
  111.   </xsl:template>
  112.  
  113.   <xsl:template match="sequence[string(sort-key) = string(preceding-sibling::sequence[1]/sort-key)]" mode="pass3"/>
  114.  
  115.   <xsl:template match="sequence" mode="pass3.1">
  116.     <xsl:apply-templates select="version" mode="pass3"/>
  117.     <xsl:apply-templates select="sequence_name" mode="pass3"/>
  118.   </xsl:template>
  119.  
  120.   <xsl:template match="sort-key" mode="pass3"/>
  121.  
  122.   <!-- =================================================== -->
  123.  
  124.   <xsl:template match="sequence" mode="pass4">
  125.     <xsl:copy>
  126.       <xsl:apply-templates select="version" mode="pass4">
  127.         <xsl:sort select="string(.)"/>
  128.       </xsl:apply-templates>
  129.       <xsl:apply-templates select="sequence_name" mode="pass4">
  130.         <xsl:sort select="string(.)"/>
  131.       </xsl:apply-templates>
  132.       <xsl:apply-templates select="hopper" mode="pass4"/>
  133.     </xsl:copy>
  134.   </xsl:template>
  135.  
  136.   <!-- =================================================== -->
  137.  
  138.   <xsl:template match="version[string()=string(preceding-sibling::version[1])]" mode="pass5"/>
  139.  
  140.   <xsl:template match="sequence_name[string()=string(preceding-sibling::sequence_name[1])]" mode="pass5"/>
  141.  
  142. </xsl:stylesheet>


 
Je suppose qu'il y a quelque chose qui fait le call <xsl:call-template name="myTemplate"/> ailleurs dans ton outil.
 
A+,


Message édité par gilou le 07-06-2019 à 18:18:11

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-06-2019 à 19:30:22    

Une version équivalente mais qui évite d'avoir une passe 3 avec une sous passe et est un peu plus facile à suivre dans sa construction.  
 

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet
  3.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.   xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.   xmlns:exsl="http://exslt.org/common"
  6.   exclude-result-prefixes="xs exsl"
  7.   version="1.0">
  8.  
  9.   <xsl:output indent="yes"/>
  10.   <xsl:strip-space elements="*"/>
  11.  
  12.   <!-- Pour tester, car un template nommé doit être appellé-->
  13.   <!--<xsl:template match="/">
  14.     <xsl:call-template name="myTemplate"/>
  15.   </xsl:template>-->
  16.  
  17.  
  18.   <xsl:template name="myTemplate">
  19.     <xsl:variable name="pass1">
  20.       <xsl:apply-templates select="/" mode="pass1"/>
  21.     </xsl:variable>
  22.     <xsl:variable name="pass2">
  23.       <xsl:apply-templates select="exsl:node-set($pass1)" mode="pass2"/>
  24.     </xsl:variable>
  25.     <xsl:variable name="pass3">
  26.       <xsl:apply-templates select="exsl:node-set($pass2)" mode="pass3"/>
  27.     </xsl:variable>
  28.     <xsl:variable name="pass4">
  29.       <xsl:apply-templates select="exsl:node-set($pass3)" mode="pass4"/>
  30.     </xsl:variable>
  31.     <xsl:variable name="pass5">
  32.       <xsl:apply-templates select="exsl:node-set($pass4)" mode="pass5"/>
  33.     </xsl:variable>
  34.     <xsl:apply-templates select="exsl:node-set($pass5)" mode="pass6"/>
  35.   </xsl:template>
  36.  
  37. <!-- copie par défaut -->
  38.   <xsl:template match="/|@*|*" mode="identity">
  39.     <xsl:copy>
  40.       <xsl:apply-templates select="@*|node()" mode="identity"/>
  41.     </xsl:copy>
  42.   </xsl:template>
  43.  
  44.   <!-- =================================================== -->
  45.   <!-- Ajout d'une clé de tri aux sequences                -->
  46.   <!-- et déplacement du version_name dans la sequence     -->
  47.   <!-- =================================================== -->
  48.   <xsl:template match="/|@*|*" mode="pass1">
  49.     <xsl:copy>
  50.       <xsl:apply-templates select="@*|node()" mode="pass1"/>
  51.     </xsl:copy>
  52.   </xsl:template>
  53.  
  54.   <xsl:template match="order" mode="pass1">
  55.     <xsl:copy>
  56.       <xsl:apply-templates mode="pass1"/>
  57.     </xsl:copy>
  58.   </xsl:template>
  59.  
  60.   <xsl:template match="version" mode="pass1">
  61.     <xsl:apply-templates select="sequence" mode="pass1">
  62.       <xsl:with-param name="version-name" select="string(version_name)"/>
  63.     </xsl:apply-templates>
  64.   </xsl:template>
  65.  
  66.   <xsl:template match="sequence" mode="pass1">
  67.     <xsl:param name="version-name"/>
  68.     <xsl:copy>
  69.       <sort-key>
  70.         <xsl:for-each select="hopper">
  71.           <xsl:if test="position()!=1 and string(.)!=''">
  72.             <xsl:value-of select="'-'"/>
  73.           </xsl:if>
  74.           <xsl:value-of select="string(.)"/>
  75.         </xsl:for-each>
  76.       </sort-key>
  77.       <version><xsl:value-of select="$version-name"/></version>
  78.       <xsl:apply-templates mode="identity"/>
  79.     </xsl:copy>
  80.   </xsl:template>
  81.  
  82.   <!-- =================================================== -->
  83.   <!-- Ajout d'un élément sort-keys contenant les valeurs  -->
  84.   <!-- des clés de tri comme d'élément sort-key, triés     -->
  85.   <!-- =================================================== -->
  86.   <xsl:template match="/|@*|*" mode="pass2">
  87.     <xsl:copy>
  88.       <xsl:apply-templates select="@*|node()" mode="pass2"/>
  89.     </xsl:copy>
  90.   </xsl:template>
  91.  
  92.   <xsl:template match="order" mode="pass2">
  93.     <xsl:copy>
  94.       <sort-keys>
  95.         <xsl:for-each select="sequence/sort-key">
  96.           <xsl:sort select="."/>
  97.           <sort-key><xsl:value-of select="."/></sort-key>
  98.         </xsl:for-each>
  99.       </sort-keys>
  100.       <xsl:apply-templates mode="identity"/>
  101.     </xsl:copy>
  102.   </xsl:template>
  103.  
  104.   <!-- =================================================== -->
  105.   <!-- Suppression des doublons de sort-keys               -->
  106.   <!-- =================================================== -->
  107.   <xsl:template match="/|@*|*" mode="pass3">
  108.     <xsl:copy>
  109.       <xsl:apply-templates select="@*|node()" mode="pass3"/>
  110.     </xsl:copy>
  111.   </xsl:template>
  112.  
  113.   <xsl:template match="order" mode="pass3">
  114.     <xsl:copy>
  115.       <xsl:apply-templates select="sort-keys" mode="pass3"/>
  116.       <xsl:apply-templates select="sequence" mode="identity"/>
  117.     </xsl:copy>
  118.   </xsl:template>
  119.  
  120.   <xsl:template match="sort-keys" mode="pass3">
  121.     <xsl:copy>
  122.       <xsl:apply-templates mode="pass3"/>
  123.     </xsl:copy>
  124.   </xsl:template>
  125.  
  126.   <xsl:template match="sort-key" mode="pass3">
  127.     <xsl:choose>
  128.       <xsl:when test="string(.)=string(preceding-sibling::sort-key[1])"/>
  129.       <xsl:otherwise>
  130.         <xsl:apply-templates select="." mode="identity"/>
  131.       </xsl:otherwise>
  132.     </xsl:choose>
  133.   </xsl:template>
  134.  
  135.   <!-- =================================================== -->
  136.   <!-- Regroupement des séquences par clé                  -->
  137.   <!-- =================================================== -->
  138.   <xsl:template match="/|@*|*" mode="pass4">
  139.     <xsl:copy>
  140.       <xsl:apply-templates select="@*|node()" mode="pass4"/>
  141.     </xsl:copy>
  142.   </xsl:template>
  143.  
  144.   <xsl:template match="order" mode="pass4">
  145.     <xsl:copy>
  146.       <xsl:for-each select="sort-keys/sort-key">
  147.         <xsl:variable name="key-value" select="string(.)"/>
  148.         <sequences>
  149.         <xsl:for-each select="/order/sequence[string(sort-key)=$key-value]">
  150.           <xsl:apply-templates select="." mode="identity"/>
  151.         </xsl:for-each>
  152.       </sequences>
  153.       </xsl:for-each>
  154.     </xsl:copy>
  155.   </xsl:template>
  156.  
  157.   <!-- =================================================== -->
  158.   <!-- Regroupement en une sequence par clé de tri         -->
  159.   <!-- Avec tri des sequence_name et version               -->
  160.   <!-- =================================================== -->
  161.   <xsl:template match="/|@*|*" mode="pass5">
  162.     <xsl:copy>
  163.       <xsl:apply-templates select="@*|node()" mode="pass5"/>
  164.     </xsl:copy>
  165.   </xsl:template>
  166.  
  167.   <xsl:template match="sequences" mode="pass5">
  168.     <sequence>
  169.       <xsl:apply-templates select="sequence/sequence_name" mode="pass5">
  170.         <xsl:sort select="string(.)"/>
  171.       </xsl:apply-templates>
  172.       <xsl:apply-templates select="sequence/version" mode="pass5">
  173.       <xsl:sort select="string(.)"/>
  174.       </xsl:apply-templates>
  175.       <xsl:apply-templates select="sequence[1]/hopper" mode="pass5"/>
  176.     </sequence>
  177.   </xsl:template>
  178.  
  179.   <!-- =================================================== -->
  180.   <!-- Les version et sequence_name sont triés             -->
  181.   <!-- On peut donc supprimer les doublons                 -->
  182.   <!-- =================================================== -->
  183.   <xsl:template match="/|@*|*" mode="pass6">
  184.     <xsl:copy>
  185.       <xsl:apply-templates select="@*|node()" mode="pass6"/>
  186.     </xsl:copy>
  187.   </xsl:template>
  188.  
  189.   <xsl:template match="version[string()=string(preceding-sibling::version[1])]" mode="pass6"/>
  190.  
  191.   <xsl:template match="sequence_name[string()=string(preceding-sibling::sequence_name[1])]" mode="pass6"/>
  192.  
  193. </xsl:stylesheet>


A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-06-2019 à 19:30:22   

Reply

Marsh Posté le 13-06-2019 à 11:19:44    

Oui, le template est appelé par un autre fichier XSL.
 
Encore merci pour le code, j'en ferai bon usage.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed