IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

XSL/XSLT/XPATH XML Discussion :

[XSLT] Filtre et regroupement selon balise


Sujet :

XSL/XSLT/XPATH XML

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3
    Par défaut [XSLT] Filtre et regroupement selon balise
    Bonjour à tous

    J'ai un problème sur lequel je me casse la tête depuis un moment déjà...
    J'ai le XML suivant fourni par un site ( donc que je ne peux pas modifier ) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    <?xml version="1.0" encoding="UTF-8" ?>
    <liste>
        <fournisseur name="toto">
            <loisir name="loisir1">
                <description>bla bla bla</description>
            </loisir>
            <loisir name="loisir2">
                <description>bla bla bla</description>
            </loisir>
                <loisir name="loisir3">
                <description>bla bla bla</description>
            </loisir>
            <service name="service1">
                <description>bla bla bla</description>
            </service>
            <service name="service2">
                <description>bla bla bla</description>
            </service>
        </fournisseur>
        <fournisseur name="titi">
            <loisir name="loisir1">
                <description>bla bla bla</description>
            </loisir>
            <loisir name="loisir4">
                <description>bla bla bla</description>
            </loisir>
            <service name="service5">
                <description>bla bla bla</description>
            </service>
            <service name="service6">
                <description>bla bla bla</description>
            </service>
        </fournisseur>
    ....
    </liste>
    Il doit être converti au format suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    <?xml version="1.0" encoding="UTF-8" ?>
    <liste>
        <fournisseur name="toto">
            <sports>
                <sport name="loisir1">
                    <description>bla bla bla</description>
                </sport>
                <sport name="loisir3">
                    <description>bla bla bla</description>
                </sport>
            </sports>
            <animations>
                <animation name="loisir2">
                    <description>bla bla bla</description>
                </animation>
            </animations>
            <services>
                <service name="service1">
                    <description>bla bla bla</description>
                </service>
            </services>
        </fournisseur>
        <fournisseur name="titi">
            <sports>
                <sport name="loisir1">
                    <description>bla bla bla</description>
                </sport>
            </sports>
            <services>
                <service name="service5">
                    <description>bla bla bla</description>
                </service>        
                <service name="service6">
                    <description>bla bla bla</description>
                </service>
            </services>
        </fournisseur>    
        ...
    </liste>
    En gros, j'ai un fichier XSL qui fait des correspondances ( avec des xsl:if ) pour savoir quel loisir est un sport ou une animation. Il me précise aussi lesquels je dois sélectionner parmi les loisirs et services dans le nouveau XML, car tout ne va pas forcément dans le nouveau XML. Et souvent le nom du service ou du loisir change.
    On peut ainsi avoir un fournisseur avec des loisirs et des services dans le premier xml, et juste des services dans le second xml...

    J'arrive actuellement à faire ce tri selon les balises avec le XSL suivant, mais le problème est que j'obtiens le XML que je mets plus bas, et donc les balises <sports> <animations> et <services> sont dupliquées, ce qui ne devrait pas être le cas !
    Je devrais regrouper tous les sports dans <sports>, toutes les animations dans <animations>, pareil pour les services, mais vu que le XML original ne suit aucun ordre, c'est très difficile...
    J'ai entendu parler de la méthode de Muench mais je ne vois pas comment l'appliquer à un regroupement par noms de balises...

    Le XSL que j'appelle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
     
    <xsl:template name="writeActivite">
     
        <!-- pour les loisirs -->
        <xsl:for-each select="loisir">
     
            <!-- on cherche le nouveau nom -->
            <xsl:variable name="newNomActivite">
                <xsl:call-template name="nomActiviteSiteToNew">
                <xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
                </xsl:call-template>
            </xsl:variable>
     
            <!-- on cherche le nouveau type : sport ou animation -->
            <xsl:variable name="typeActiviteNew">
                <xsl:call-template name="typeActiviteSiteToNew">
                <xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
                </xsl:call-template>
            </xsl:variable>
     
            <!-- si on a trouvé quelque chose dans la correspondance -->
            <xsl:if test="$newNomActivite != '' and $typeActiviteNew != ''"> 
                <!-- on écrit le bloc -->
                <xsl:call-template name="writeBlocActivite">
                    <xsl:with-param name="typeActivite" select="$typeActiviteNew"></xsl:with-param>
                    <xsl:with-param name="nomActivite" select="$newNomActivite"></xsl:with-param>
                </xsl:call-template>
            </xsl:if>
     
        </xsl:for-each>
     
        <!-- pareil pour les services -->
        <xsl:for-each select="service">
     
            <!-- on cherche le nouveau nom -->
            <xsl:variable name="newNomActivite">
                <xsl:call-template name="nomActiviteSiteToNew">
                <xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
                </xsl:call-template>
            </xsl:variable>
     
            <!-- on cherche le nouveau type ( sert à rien dans l'absolu, c'est juste un filtre ) -->
            <xsl:variable name="typeActiviteNew">
                <xsl:call-template name="typeActiviteSiteToNew">
                <xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
                </xsl:call-template>
            </xsl:variable>
     
            <!-- si on a trouvé quelque chose dans la correspondance -->
            <xsl:if test="$newNomActivite != '' and $typeActiviteNew != ''"> 
                <!-- on écrit le bloc -->
                <xsl:call-template name="writeBlocActivite">
                    <xsl:with-param name="typeActivite" select="$typeActiviteNew"></xsl:with-param>
                    <xsl:with-param name="nomActivite" select="$newNomActivite"></xsl:with-param>
                </xsl:call-template>
            </xsl:if>
     
        </xsl:for-each>
     
    </xsl:template>
     
    <!-- ecriture du nouveau bloc -->
    <xsl:template name="writeBlocActivite">
    <xsl:param name="typeActivite" />
    <xsl:param name="nomActivite" />
     
        <xsl:element name="{concat($typeActivite, 's')}"> <!-- c'est là le problème !!! Où mettre cela ? -->
            <xsl:element name="{$typeActivite}">
            <xsl:attribute name="name"><xsl:value-of select="$nomActivite"/></xsl:attribute>
                <xsl:element name="description">
                    <xsl:value-of select="translate(description,'
    ',' ')" />
                </xsl:element>
            </xsl:element>
        </xsl:element>
     
    </xsl:template>
    Et ce que j'obtiens ( donc mal trié ) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <?xml version="1.0" encoding="UTF-8" ?>
    <liste>
        <fournisseur name="toto">
            <sports>
                <sport name="loisir1">
                    <description>bla bla bla</description>
                </sport>
            </sports>
            <sports> <!-- ne devrait pas être là -->
                <sport name="loisir3">
                    <description>bla bla bla</description>
                </sport>
            </sports>
            <animations>
                <animation name="loisir2">
                    <description>bla bla bla</description>
                </animation>
            </animations>
            <services>
                <service name="service1">
                    <description>bla bla bla</description>
                </service>
            </services>
        </fournisseur>
        <fournisseur name="titi">
            <sports>
                <sport name="loisir1">
                    <description>bla bla bla</description>
                </sport>
            </sports>
            <services>
                <service name="service5">
                    <description>bla bla bla</description>
                </service>
            </services>
            <services> <!-- pareil -->
                <service name="service6">
                    <description>bla bla bla</description>
                </service>
            </services>
        </fournisseur>    
        ...
    </liste>
    Merci d'avance pour votre aide !

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    Ça m'a l'air assez compliqué... Et je ne crois pas qu'un Muench soit la solution.

    A priori, ce que tu dois faire, c'est classer, sur papier, l'ordre des activités qui existeront dans ton document produit.

    Pour chacune de ces activités là, tu fais un for-each qui sélectionne tout ce qu'il faut mettre dedans à partir du XML de départ.

    En gros :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <sports>
      <xsl:for-each select="loisir[@name = 'loisir1' or @name = 'loisir3']">
      </xsl:for-each>
      <xsl:for-each select="loisir[@name = 'loisir2']">
      </xsl:for-each>
    </sports>
    <animations>
      <xsl:for-each select="service[@name = 'service3']">
      </xsl:for-each>
      <xsl:for-each select="loisir[@name = 'loisir2']|service[@name='service1']">
      </xsl:for-each>
    </animations>
    Et adapter à tes besoins.

    Si par hasard ça fait des XPath bien trop compliqués et difficiles à maintenir, il y a moyen d'ajouter des indirections et d'éclater avec des call-templates :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <sports>
      <xsl:call-template name="insert-sports"/>
    </sports>
     
    [...]
     
    <!--
     Un template est appliqué à tous les éléments du fournisseur,
     puis on passe au template suivant.
     Donc, un tempalte vérifie si l'élément le concerne.
     Si oui, il génère ce qu'il faut. Si non, il ne fait rien.
    -->
    <xsl:template name="insert-sports">
      <xsl:for-each select="*">
        <xsl:call-template name="insert-sport-loisir1"
      </xsl:for-each>
      <xsl:for-each select="*">
        <xsl:call-template name="insert-sport-loisir2"
      </xsl:for-each>
      <xsl:for-each select="*">
        <xsl:call-template name="insert-sport-loisir3"
      </xsl:for-each>
    </xsl:template>
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3
    Par défaut
    Merci beaucoup pour tes indications, j'ai pu résoudre mon problème avec elles et un peu de réflexion

    Voici le XSL que j''ai fait au final et qui marche bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    <xsl:template name="writeActivite">
    	<sports>
    		<xsl:call-template name="insert-sports" />
    	</sports>
    	<animations>
    		<xsl:call-template name="insert-animation" />
    	</animations>
    	<services>
    		<xsl:call-template name="insert-service" />
    	</services>	
    </xsl:template>
     
     
    <xsl:template name="insert-sports">
    	<!--  tous les loisirs qui sont des sports -->
    	<xsl:for-each select="loisir[@name='loisir1' or @name='loisir2' or @name='loisir5']">
    		<!-- on appelle le template qui va ecrire le bloc d'infos -->
    		<!-- on lui passe le type de ce qu'on passe, et le nom -->
    		<xsl:call-template name="writeBlocActivite">
    			<xsl:with-param name="typeActivite">sport</xsl:with-param>
    			<xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
    		</xsl:call-template>
    	</xsl:for-each>
    </xsl:template>
     
    <xsl:template name="insert-animation">
    	<!--  tous les loisirs qui sont des animations -->
    	<xsl:for-each select="loisir[@name='loisir3' or @name='loisir7']">
    		<xsl:call-template name="writeBlocActivite">
    			<xsl:with-param name="typeActivite">animation</xsl:with-param>
    			<xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
    		</xsl:call-template>
    	</xsl:for-each>
    </xsl:template>
     
    <xsl:template name="insert-service">
    	<!--  tous les services -->
    	<xsl:for-each select="service[@name='service1' or @name='service3']">
    		<xsl:call-template name="writeBlocActivite">
    			<xsl:with-param name="typeActivite">service</xsl:with-param>
    			<xsl:with-param name="nomActivite" select="@name"></xsl:with-param>
    		</xsl:call-template>
    	</xsl:for-each>
    </xsl:template>
     
     
    <!-- ecriture du bloc -->
    <xsl:template name="writeBlocActivite">
    <xsl:param name="typeActivite" />
    <xsl:param name="nomActivite" />
     
    	<!-- on va chercher le nouveau nom -->
    	<xsl:variable name="nomActiviteNew">
    		<xsl:call-template name="nomActiviteSiteToNew">
    		<xsl:with-param name="nom" select="$nomActivite" />
    		</xsl:call-template>
    	</xsl:variable>	
     
    	<xsl:element name="{$typeActivite}">
    	<xsl:attribute name="name"><xsl:value-of select="$nomActiviteNew"/></xsl:attribute>
    		<xsl:element name="description">
    			<xsl:value-of select="translate(description,'
    ',' ')" />
    		</xsl:element>
    	</xsl:element>
     
    </xsl:template>
    Le XSL qui fait les correspondances des noms ( mis dans un autre fichier par convenance ) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <xsl:template name="nomActiviteSiteToNew">
    	<xsl:param name="nom" />
    	<xsl:choose>
    		<!-- sports -->
    		<xsl:when test="$nom='loisir1'">
    			<xsl:text>newloisir1</xsl:text>
    		</xsl:when>
    		<xsl:when test="$nom='loisir2'">
    			<xsl:text>newloisir2</xsl:text>
    		</xsl:when>	
    		...
     
    		<!-- animation -->
    		<xsl:when test="$nom='loisir5'">
    			<xsl:text>newloisir5</xsl:text>
    		</xsl:when>	
     
    		<!-- service -->
    		<xsl:when test="$nom='service1'">
    			<xsl:text>newservice1</xsl:text>
    		</xsl:when>		
    		<xsl:when test="$nom='service2'">
    			<xsl:text>newservice2</xsl:text>
    		</xsl:when>			
    	</xsl:choose>
    </xsl:template>
    J'obtiens bien le XML demandé avec tous les sports groupés dans <sports>, toutes les animations groupées dans <animations>... et les nouveaux noms sont transcrits ^^

    Au niveau maintenance ça reste jouable car en cas d'ajout il faudra juste ajouter le xpath dans insert-sports ou un autre, et rajouter le nouveau nom à la liste des noms.

    Merci pour ton aide, je met le sujet en résolu

  4. #4
    Rédacteur

    Avatar de Erwy
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2003
    Messages
    4 967
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Novembre 2003
    Messages : 4 967
    Par défaut
    Je crois qu'il y a un problème de conception.
    Tu ne devrais tout simplement pas faire tes regroupements via des xsl:if.


    Tu confonds traitement et donnée.
    Ce que tu dois faire c'est un deuxième XML qui gérera les correspondances entre nom et type d'activité, exactement comme une table d'association dans un modèle relationnel.
    Ce XML sera appelé dans ton XSLT via la fonction document(...) et là tu pourras avoir un XSLT totalement générique.
    C'est aussi performant d'un point de vue traitement et bien plus d'un point de vue maintenance (il n'y a pas besoin de connaitre XSLT pour modifier le xml).

    Ce n'est pas de la théorie, je pratique assez couramment ce type de manipulation

  5. #5
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    J'appellerais pas ça un problème de conception.
    J'appellerais ça "faire simplement en attendant de savoir faire maintenable."

    La solution propre et générique inclut quand même de nouveaux problèmes à résoudre. Je la préférerais parce que je sais déjà me débrouiller tout seul, mais partir là-dessus quand on savait même pas dans quelle direction aller, c'est couillu.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Rédacteur

    Avatar de Erwy
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2003
    Messages
    4 967
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Novembre 2003
    Messages : 4 967
    Par défaut
    Citation Envoyé par thelvin Voir le message
    J'appellerais pas ça un problème de conception.
    J'appellerais ça "faire simplement en attendant de savoir faire maintenable."
    M'ouais...
    Enfin il n'y a rien d'inventé la dedans, comme pour un sgbd , c'est un pur problème de modélisation des données avec table d'association.

    Je suis gentils parce que pas mal de personnes débarquent sur ces technos et qu'il n'y a pas assez de retour sur les modélisations (j'ai un tuto en cours sur le sujet), mais si on travaillait sur un sgbdr et qu'on me disait qu'on a fait un traitement de ce type dans le code plutôt que de modifier le modèle de donnée, je passe le coupable par la fenêtre.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [XSLT] Regroupement de balises
    Par nadsky dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 03/06/2009, 11h43
  2. [XSLT] regroupement de balises séquentielles
    Par grafikeyes dans le forum XSL/XSLT/XPATH
    Réponses: 3
    Dernier message: 22/11/2007, 13h56
  3. [XSLT] Regroupement de balises
    Par maa dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 02/10/2007, 06h26
  4. [XSLT]Problèmes pour regrouper selon l'attribut
    Par gharl dans le forum XSL/XSLT/XPATH
    Réponses: 7
    Dernier message: 21/08/2006, 21h39
  5. Filtre d'affichage de balise selon leur attributs
    Par Norely dans le forum XML/XSL et SOAP
    Réponses: 10
    Dernier message: 03/04/2003, 14h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo