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 :

Rechercher les noeuds vides et ceux qui n'ont pas d'attributs


Sujet :

XSL/XSLT/XPATH XML

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2012
    Messages : 9
    Points : 10
    Points
    10
    Par défaut Rechercher les noeuds vides et ceux qui n'ont pas d'attributs
    Bonjour,

    Suite à la discussion que j'avais ouverte pour identifier les nœuds vides d'un fichier XML : http://www.developpez.net/forums/d14...-noeuds-vides/
    J'ai une nouvelle question :

    Il me faudrait supprimer les nœuds vides d'un fichier XML, mais pas les nœuds vides ayant des attributs.

    Exemple :

    Voici le xml en entrée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="UTF-8"?>
    <root>
         <tag1>
            un peu de texte
         </tag1>
         <tag2>
            encore du texte
         </tag2>
         <tag3>
            <tag3child1>test</tag3child1>
    	<tag3child2 attribut1="test"/>
    	<tag3child3/>
         </tag3>
    </root>
    Et le résultat attendu serait donc que <tag3child3> disparaisse :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="UTF-8"?>
    <root>
         <tag1>
            un peu de texte
         </tag1>
         <tag2>
            encore du texte
         </tag2>
         <tag3>
            <tag3child1>test</tag3child1>
    	<tag3child2 attribut1="test"/>
         </tag3>
    </root>
    J'utilise le script XSLT suivant, en rajoutant dans ma condition le fait qu'il recherche les nœuds vides qui n'ont pas d'attributs.
    J'essaie avec ceci : and @*= '' mais apparemment ce n'est pas la bonne solution / syntaxe.

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     
    <!--							-->
    <!-- Remove elements if empty	-->
    <!--							-->
    <xsl:template match="@*|node()">
    	<xsl:copy>
    		<xsl:apply-templates select="@*|node()"/>
    	</xsl:copy>
    </xsl:template>
     
    <xsl:template match="tag3child1|tag3child2|tag3child3">
    	<xsl:if test="normalize-space(string(.)) != '' and @* != ''">
    		<xsl:copy>
    			<xsl:apply-templates select="@*|node()"/>
    		</xsl:copy>
    	</xsl:if>
    </xsl:template>
     
    </xsl:stylesheet>
    Ce n'est pas ce que j'espérais puisque j'obtiens le résultat suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="UTF-8"?>
    <root>
         <tag1>
            un peu de texte
         </tag1>
         <tag2>
            encore du texte
         </tag2>
         <tag3>
         <!-- tag3 est complètement vide -->
         </tag3>
    </root>
    Le tag 3 est complètement vide, j'avoue que mes recherches se sont révélées plutôt infructueuses.
    Un peu d'aide, ou une piste serait la bienvenue

    D'avance merci

  2. #2
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2012
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    J'ai également essayé avec : and node()[not(@*)]

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     
    <!--							-->
    <!-- Remove elements if empty	-->
    <!--							-->
    <xsl:template match="@*|node()">
    	<xsl:copy>
    		<xsl:apply-templates select="@*|node()"/>
    	</xsl:copy>
    </xsl:template>
     
    <xsl:template match="tag3child1|tag3child2|tag3child3">
    	<xsl:if test="normalize-space(string(.)) != '' and node()[not(@*)]">
    		<xsl:copy>
    			<xsl:apply-templates select="@*|node()"/>
    		</xsl:copy>
    	</xsl:if>
    </xsl:template>
     
    </xsl:stylesheet>
    Mais ce n'est toujours pas le résultat escompté :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="UTF-8"?>
    <root>
         <tag1>
            un peu de texte
         </tag1>
         <tag2>
            encore du texte
         </tag2>
         <tag3>
            <tag3child1>test</tag3child1>
            <!-- tag3child2 n'aurait pas du être supprimé -->
         </tag3>
    </root>

  3. #3
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 565
    Points : 21 630
    Points
    21 630
    Par défaut
    Tu es sur la bonne voie mais malheureusement tu penses en sens inverse.

    - Tu veux éliminer des éléments, mais pas tous. Les éléments que tu veux garder, c'est si ils contiennent du texte, ou si ils ont des attributs.
    Ou. C'est un ou. C'est quoi ce and ?

    - Toujours en sens inverse, ceux que tu veux garder c'est s'ils ont des attributs. "Les attributs" c'est @*, ça c'est ok... Alors pourquoi tu mets un not() autour -_-° ?

    - Et enfin, ça c'est juste une erreur de syntaxe, node() ça veut dire "sélectionne-moi tous les nœuds enfants." ... Pourquoi des nœuds enfants ? C'est le nœud en cours qui t'intéresse, pas ses enfants.
    Tu n'as pas besoin de faire une sélection avec un prédicat, tu es déjà dans un if qui vérifie une condition.

    -- Conclusion,

    tu cherches à faire quelque chose de la forme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    test="il_y_a_du_texte or il_y_a_des_attributs"
    et "il y a des attributs" c'est la même chose que "sélectionne-moi tous les attributs, s'il y en a, c'est vrai, s'il y en a pas, c'est faux"

    ce qui s'écrit tout simplement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    test="il_y_a_du_texte or @*"

  4. #4
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2012
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    Bonjour thelvin, et merci de ta réponse

    Je ne m'explique les erreurs sur le "not" et le "and", elles me paraissent tellement évidentes maintenant que tu m'as mis le nez dessus.

    Merci pour la précision sur le node() dont j'avais mal compris la signification et qui pour moi désignait les nœuds de tout le document. Si je comprend bien cela désigne donc les nœuds enfant de l'élément en cours.

    Je te confirme que le script XSLT marche bien avec le petit exemple que je t'ai donné.

    Par contre il ne marche plus si je complexifie mon fichier d'entrée :

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <root date="2014-08-13T04:01:42">
     
    	<nodeNumberAttribute>
    		<nodeNumber number="1258"/>
    		<nodeNumber number="2354"/>
    		<nodeNumber number="2452"/>
    		<nodeNumber number="2631"/>
    		<nodeNumber number="2655"/>
    		<nodeNumber number="2738"/>
    		<nodeNumber number="2739"/>
    		<nodeNumber number="3142"/>
    		<nodeNumber number="4629"/>
    		<nodeNumber number="4665"/>
    		<nodeNumber number="4695"/>
    		<nodeNumber number="4755"/>
    		<nodeNumber number="4774"/>
    		<nodeNumber number="4790"/>
    	</nodeNumberAttribute>
     
    	<nodeExample>
    		<childExampleAttribute>
    			<nodeBooleanAttribute>
    				<!-- les noeuds ont des attributs et ne doivent donc pas disparaitre -->
    				<nodeBoolean valeur="true"/>
    				<nodeBoolean valeur="true"/>
    				<nodeBoolean valeur="false"/>
    				<nodeBoolean valeur="true"/>
    				<nodeBoolean valeur="true"/>
    				<nodeBoolean valeur="false"/>
    				<nodeBoolean valeur="true"/>
    				<nodeBoolean valeur="false"/>
    				<nodeBoolean valeur="true"/>
    			</nodeBooleanAttribute>
    		</childExampleAttribute>
     
    		<!-- le noeud suivant devrait disparaitre -->
    		<childExampleEmpty>
    			<nodeEmpty/>
    			<nodeBooleanAttribute/>
    		</childExampleEmpty>
    		<!--                                      -->
     
    	</nodeExample>
     
    	<nodeDateAttribute>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    	</nodeDateAttribute>
     
    </root>
    et que je lui passe ce script en précisant que je veux que le "nettoyage" se fasse également sur les noeuds parents :

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     
    <!--							-->
    <!-- Remove elements if empty	-->
    <!--							-->
    <xsl:template match="@*|node()">
    	<xsl:copy>
    		<xsl:apply-templates select="@*|node()"/>
    	</xsl:copy>
    </xsl:template>
     
    <xsl:template match="nodeEmpty|nodeBooleanAttribute|childExampleEmpty|childExampleAttribute|nodeExample">
    	<xsl:if test="normalize-space(string(.)) != '' or @*">
    		<xsl:copy>
    			<xsl:apply-templates select="@*|node()"/>
    		</xsl:copy>
    	</xsl:if>
    </xsl:template>
     
    </xsl:stylesheet>
    Voici ce que j'obtiens :

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <root date="2014-08-13T04:01:42">
     
    	<nodeNumberAttribute>
    		<nodeNumber number="1258"/>
    		<nodeNumber number="2354"/>
    		<nodeNumber number="2452"/>
    		<nodeNumber number="2631"/>
    		<nodeNumber number="2655"/>
    		<nodeNumber number="2738"/>
    		<nodeNumber number="2739"/>
    		<nodeNumber number="3142"/>
    		<nodeNumber number="4629"/>
    		<nodeNumber number="4665"/>
    		<nodeNumber number="4695"/>
    		<nodeNumber number="4755"/>
    		<nodeNumber number="4774"/>
    		<nodeNumber number="4790"/>
    	</nodeNumberAttribute>
     
    	<!-- Le noeud nodeExample a entiérement été supprimé -->
     
    	<nodeDateAttribute>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    		<nodeDate date="2014-09-10T04:01:39"/>
    	</nodeDateAttribute>
     
    </root>
    Autre question :

    Si j'ai bien compris le XSL agit avec de la récursivité. J'ai encore du mal à comprendre comment cela marche.
    Mais il me semblait que si je précisais dans le match du template le noeud parent "nodeExample" il agirait sur tous les noeuds enfants et supprimerait ceux qui ne répondent pas à ma condition.
    Du coup j'ai également testé avec ce script légèrement modifié :

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     
    <!--							-->
    <!-- Remove elements if empty	-->
    <!--							-->
    <xsl:template match="@*|node()">
    	<xsl:copy>
    		<xsl:apply-templates select="@*|node()"/>
    	</xsl:copy>
    </xsl:template>
     
    <xsl:template match="nodeExample">
    	<xsl:if test="normalize-space(string(.)) != '' or @*">
    		<xsl:copy>
    			<xsl:apply-templates select="@*|node()"/>
    		</xsl:copy>
    	</xsl:if>
    </xsl:template>
     
    </xsl:stylesheet>
    Mais j'ai le même résultat qu'avec le script précédent.

    Je pense que mon raisonnement est faux (d'ailleurs les résultats le prouvent ).
    Je me tourne donc à nouveau vers toi, ou des personnes qui auraient des éléments de réponse.

    Par avance merci

  5. #5
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 565
    Points : 21 630
    Points
    21 630
    Par défaut
    Citation Envoyé par Egidius Voir le message
    Par contre il ne marche plus si je complexifie mon fichier d'entrée :
    Ah... Ben oui, on décide de garder :
    - s'il y a du texte dans l'élément
    - ou si l'élément contient des attributs.

    Est-ce qu'il y a du texte dans l'élément => non.
    Est-ce que l'élément contient des attributs => non. Ses descendants en contiennent, mais pas lui.

    Conclusion => on le garde pas et on ignore ce qu'il contient.

    Il faut donc changer la condition, pour ne pas s'occuper que des attributs de l'élément en cours, mais les attributs de tous ses descendants aussi.
    "Tous les descendants" c'est .//*
    Donc "Les attributs de tous les descendants" c'est .//*/@*
    On peut aussi l'écrire .//*[@*] "tous les descendants qui ont un attribut."

    donc ça donne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    test="il_y_a_du_texte or @* or .//*/@*"
    Citation Envoyé par Egidius Voir le message
    Autre question :

    Si j'ai bien compris le XSL agit avec de la récursivité. J'ai encore du mal à comprendre comment cela marche.
    Mais il me semblait que si je précisais dans le match du template le noeud parent "nodeExample" il agirait sur tous les noeuds enfants et supprimerait ceux qui ne répondent pas à ma condition.
    XSLT est essentiellement un super-CSS. Il repère les éléments comme CSS le ferait, mais il est capable de les modifier et de modifier leur contenu, au lieu de juste leur donner une présentation.

    Un template ne s'applique que quand il détecte un élément qui correspond au match. Pas pour les enfants d'un tel élément.
    Mais un template peut contenir l'instruction apply-templates, qui fait exactement ce qu'elle dit : regarder les enfants de l'élément en cours, et appliquer dessus les règles de la feuille XSLT.

    Typiquement "quand tu trouves un élément <toto> remplace-le par un élément <tata chose='machin'>. Et pour son contenu, applique les règles de la feuille de style au contenu du <toto>."
    C'est résursif, oui, mais seulement parce qu'on a rencontré une instruction apply-templates. Si on ne la rencontre pas, le contenu de l'élément est ignoré.

    À noter que si un élément A n'a pas de parent pour lequel un template s'applique, alors XSLT va regarder s'il y a template qui s'appliquer à cet élément A. Autrement dit, il existe implicitement un template par défaut :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <xsl:template match="node()">
      <xsl:apply-templates/>
    </xsl:template>
    C'est pour ça qu'il n'est pas nécessaire de préciser pour tous les éléments, "oui, je veux que tu traites le contenu de cet élément." Il n'est nécessaire de le faire que pour les éléments dont tu as changé le traitement par défaut.

  6. #6
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2012
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    Le prédicat "normalize-space(string(.)) != '' or .//*[@*]" marche effectivement et le script me renvoi bien ce que j'attendais.

    Avec ton exemple je pense avoir à peu prêt saisi le principe des templates.
    Je vais continuer à bosser dessus, certaines choses restent encore un peu floues.

    En tout cas merci pour ta patience et tes conseils, j'y vois beaucoup plus clair qu'au début

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

Discussions similaires

  1. [XL-2010] Lister les fichier d'un répertoire qui n'ont pas encore été listés
    Par QuestVba dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 27/10/2014, 10h03
  2. A ceux qui n'ont pas migré vers VB.NET
    Par bidou dans le forum VB 6 et antérieur
    Réponses: 153
    Dernier message: 12/12/2013, 13h33
  3. Réponses: 5
    Dernier message: 31/08/2009, 09h46
  4. Utiliser Infopath pour ceux qui n'ont pas Infopath
    Par tom1855 dans le forum InfoPath
    Réponses: 2
    Dernier message: 16/08/2009, 23h06
  5. [TV] A voir pour ceux qui n'ont pas encore vu :p (Les lascars)
    Par Interruption13h dans le forum Films & TV
    Réponses: 7
    Dernier message: 26/06/2007, 14h29

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