53 votes

XSLT - supprimer les espaces dans les modèles

J'utilise XML pour stocker une petite liste de contacts et j'essaie d'écrire un modèle XSL qui la transformera en un fichier CSV. Le problème que je rencontre concerne les espaces dans le fichier de sortie.

Le résultat :

Friend, John, Smith, Home,
        123 test,
       Sebastopol,
       California,
       12345,
     Home 1-800-123-4567, Personal john.smith@gmail.com

J'ai indenté/espacé le fichier XML source et le modèle XSL associé pour en faciliter la lecture et le développement, mais tous ces espaces blancs supplémentaires se retrouvent dans le résultat. Le XML lui-même n'a pas d'espace blanc supplémentaire à l'intérieur des nœuds, juste à l'extérieur pour le formatage, et il en va de même pour le XSLT.

Pour que le fichier CSV soit valide, chaque entrée doit être sur sa propre ligne, sans être interrompue. Outre la suppression de tout espace blanc supplémentaire dans le XML et le XSLT (ce qui en fait une seule longue ligne de code), existe-t-il un autre moyen de se débarrasser des espaces blancs dans la sortie ?

Edit : Voici un petit échantillon XML :

<PHONEBOOK>
    <LISTING>
        <FIRST>John</FIRST>
        <LAST>Smith</LAST>
        <ADDRESS TYPE="Home">
            <STREET>123 test</STREET>
            <CITY>Sebastopol</CITY>
            <STATE>California</STATE>
            <ZIP>12345</ZIP>
        </ADDRESS>
        <PHONE>1-800-123-4567</PHONE>
        <EMAIL>john.smith@gmail.com</EMAIL>
        <RELATION>Friend</RELATION>
    </LISTING>
</PHONEBOOK>

Et voici le XSLT :

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />

 <xsl:template match="/">
   <xsl:for-each select="//LISTING">
    <xsl:value-of select="RELATION" /><xsl:text>, </xsl:text>
    <xsl:value-of select="FIRST" /><xsl:text>, </xsl:text>
    <xsl:value-of select="LAST" /><xsl:text>, </xsl:text>

    <xsl:if test="ADDRESS">
     <xsl:for-each select="ADDRESS">
       <xsl:choose>
        <xsl:when test="@TYPE">
         <xsl:value-of select="@TYPE" />,
        </xsl:when>
            <xsl:otherwise>
            <xsl:text>Home </xsl:text>
            </xsl:otherwise>
       </xsl:choose>
       <xsl:value-of select="STREET" />,
       <xsl:value-of select="CITY" />,
       <xsl:value-of select="STATE" />,
       <xsl:value-of select="ZIP" />,
     </xsl:for-each>
    </xsl:if>

    <xsl:for-each select="PHONE">
      <xsl:choose>
       <xsl:when test="@TYPE">
        <xsl:value-of select="@TYPE" />  
       </xsl:when>
       <xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
      </xsl:choose>
     <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
    </xsl:for-each>

    <xsl:if test="EMAIL">
     <xsl:for-each select="EMAIL">
      <xsl:choose>
       <xsl:when test="@TYPE">
        <xsl:value-of select="@TYPE" /><xsl:text  > </xsl:text> 
       </xsl:when>
       <xsl:otherwise><xsl:text  >Personal </xsl:text></xsl:otherwise>
      </xsl:choose>
      <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
     </xsl:for-each>
    </xsl:if>
    <xsl:text>&#10;&#13;</xsl:text>
   </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

4 votes

Pourquoi utilisez-vous disable-output-escaping sur chaque <xsl:text> ? Vous n'avez pas besoin de cela pour <xsl:output method='text'> . Extrait de la spécification XSLT 1.0 : "La méthode de sortie du texte ignore l'attribut disable-output-escaping, puisqu'elle n'effectue aucun échappement en sortie."

108voto

Tomalak Points 150423

En XSLT, les espaces blancs sont préservés par défaut, car il peut très bien s'agir de données pertinentes.

La meilleure façon d'éviter les espaces blancs non désirés dans le résultat est de ne pas en créer en premier lieu. Ne le faites pas :

<xsl:template match="foo">
  foo
</xsl:template>

parce que c'est "\n··foo\n" du point de vue du processeur. Faites plutôt

<xsl:template match="foo">
  <xsl:text>foo</xsl:text>
</xsl:template>

Les espaces blancs dans la feuille de style sont ignorés tant qu'ils se trouvent entre les éléments XML uniquement. En d'autres termes, n'utilisez jamais de texte "nu" dans votre code XSLT, mais enfermez-le toujours dans un élément.

De plus, l'utilisation d'un nom non spécifique :

<xsl:apply-templates />

est problématique, car la règle XSLT par défaut pour les noeuds de texte dit "copier les vers la sortie". Cela s'applique également aux noeuds "white-space-only". Par exemple :

<xml>
  <data> value </data>
</xml>

contient trois nœuds de texte :

  1. "\n··" (juste après <xml> )
  2. "·value·"
  3. " \n" (juste avant </xml> )

Pour éviter que les numéros 1 et 3 ne se glissent dans la sortie (ce qui est la raison la plus courante des espaces non désirés), vous pouvez remplacer la règle par défaut pour les nœuds de texte en déclarant un modèle vide :

<xsl:template match="text()" />

Tous les nœuds de texte sont désormais muets et la sortie de texte doit être créée explicitement :

<xsl:value-of select="data" />

Pour supprimer les espaces blancs d'une valeur, vous pouvez utiliser la fonction normalize-space() Fonction XSLT :

<xsl:value-of select="normalize-space(data)" />

Mais attention, car la fonction normalise tout espace blanc trouvé dans la chaîne de caractères, par ex. "·value··1·" deviendrait "value·1" .

En outre, vous pouvez utiliser le <xsl:strip-space> y <xsl:preserve-space> bien que cela ne soit généralement pas nécessaire (et personnellement, je préfère la gestion explicite des espaces blancs comme indiqué ci-dessus).

11voto

Noah Heldman Points 2817

Par défaut, les modèles XSLT ont <xsl:preserve-space> ce qui permet de conserver les espaces dans la sortie. Vous pouvez ajouter <xsl:strip-space elements="*"> pour le dire à où supprimer les espaces blancs.

Vous devrez peut-être aussi inclure une directive normalize-space, comme ceci :

<xsl:template match="text()"><xsl:value-of select="normalize-space(.)"/></xsl:template> 

Voici un exemple pour l'espace préservé/strip des écoles W3 .

11 votes

W3 Schools != W3 Reference, la référence W3 se trouve ici -> w3.org/TR/xslt#strip

2voto

David Andres Points 13569

Pour ce qui est de supprimer les tabulations tout en conservant des lignes distinctes, j'ai essayé l'approche XSLT 1.0 suivante, qui fonctionne plutôt bien. L'utilisation de la version 1.0 ou 2.0 dépend largement de la plate-forme que vous utilisez. Il semble que la technologie .NET dépende toujours de XSLT 1.0, et vous êtes donc limité à des modèles extrêmement désordonnés (voir ci-dessous). Si vous utilisez Java ou autre chose, veuillez vous référer à l'approche XSLT 2.0, beaucoup plus propre, indiquée tout en bas.

Ces exemples sont destinés à être étendus par vous pour répondre à vos besoins spécifiques. J'utilise ici les onglets comme exemple, mais cela devrait être suffisamment générique pour être extensible.

XML :

<?xml version="1.0" encoding="UTF-8"?>
<text>
        adslfjksdaf

                dsalkfjdsaflkj

            lkasdfjlsdkfaj
</text>

...et le modèle XSLT 1.0 (nécessaire si vous utilisez .NET) :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet  
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">   
 <xsl:template name="search-and-replace">
   <xsl:param name="input"/>
   <xsl:param name="search-string"/>
   <xsl:param name="replace-string"/>
   <xsl:choose>
    <xsl:when test="$search-string and 
                    contains($input,$search-string)">
       <xsl:value-of
           select="substring-before($input,$search-string)"/>
       <xsl:value-of select="$replace-string"/>
       <xsl:call-template name="search-and-replace">
         <xsl:with-param name="input"
               select="substring-after($input,$search-string)"/>
         <xsl:with-param name="search-string"
               select="$search-string"/>
         <xsl:with-param name="replace-string"
               select="$replace-string"/>
       </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$input"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:template>                
  <xsl:template match="text">
   <xsl:call-template name="search-and-replace">
     <xsl:with-param name="input" select="text()" />
     <xsl:with-param name="search-string" select="'&#x9;'" />
     <xsl:with-param name="replace-string" select="''" />
   </xsl:call-template>    
  </xsl:template>
</xsl:stylesheet>

XSLT 2.0 rend cela trivial avec la fonction replace fonction :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs"
      version="2.0">
 <xsl:template match="text">
  <xsl:value-of select="replace(text(), '&#x9;', '')" />
 </xsl:template>
</xsl:stylesheet>

1voto

Nick Groznykh Points 19

Ma réponse précédente est erronée, toutes les virgules doivent être affichées via la balise 'text'.

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="/PHONEBOOK">
        <xsl:for-each select="LISTING">
            <xsl:value-of select="RELATION" /><xsl:text>, </xsl:text>
            <xsl:value-of select="FIRST" /><xsl:text>, </xsl:text>
            <xsl:value-of select="LAST" /><xsl:text>, </xsl:text>

                <xsl:for-each select="ADDRESS">
                    <xsl:choose>
                        <xsl:when test="@TYPE">
                            <xsl:value-of select="@TYPE" /><xsl:text>,</xsl:text>
                        </xsl:when>
                        <xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
                    </xsl:choose>
                <xsl:value-of select="STREET/text()" /><xsl:text>,</xsl:text>
                    <xsl:value-of select="CITY/text()" /><xsl:text>,</xsl:text>
                    <xsl:value-of select="STATE/text()" /><xsl:text>,</xsl:text>
                    <xsl:value-of select="ZIP/text()" /><xsl:text>,</xsl:text>
                </xsl:for-each>

            <xsl:for-each select="PHONE">
                <xsl:choose>
                    <xsl:when test="@TYPE">
                        <xsl:value-of select="@TYPE" />  
                    </xsl:when>
                    <xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
                </xsl:choose>
                <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
            </xsl:for-each>

            <xsl:if test="EMAIL">
                <xsl:for-each select="EMAIL">
                    <xsl:choose>
                        <xsl:when test="@TYPE">
                            <xsl:value-of select="@TYPE" /><xsl:text  > </xsl:text> 
                        </xsl:when>
                        <xsl:otherwise><xsl:text  >Personal </xsl:text></xsl:otherwise>
                    </xsl:choose>
                    <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
                </xsl:for-each>
            </xsl:if>
            <xsl:text>&#10;&#13;</xsl:text>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="text()|@*">
        <xsl:text>-</xsl:text>
    </xsl:template>

</xsl:stylesheet>

1voto

Cylian Points 4522

Cette réponse peut ne pas répondre directement au problème. Mais une façon générale de résoudre ce problème. Créez une règle modèle :

<xsl:template name="strip-space">
    <xsl:param name="data"/>
    <xsl:value-of select="normalize-space($data)"/>
</xsl:template>

Maintenant, appelez-le pour enlever l'excès d'espace blanc :

<xsl:template match="my-element">
    <xsl:call-template name="strip-space">
        <xsl:with-param name="data">
            <xsl:apply-templates/>
        </xsl:with-param>
    </xsl:call-template>
</xsl:template>

Par exemple, considérez le fragment XML ci-dessous :

<?xml version="1.0" encoding="UTF-8"?>
<test>
    <my-element>
        <e1>some text</e1> <e2>some other text</e2> <e3>some other text</e3>
    </my-element>
</test>

Et si quelqu'un aime le convertir en texte ci-dessous :

{test{my-element{e1some text} {e2some other text} {e3some other text}}}

Maintenant vient la feuille de style :

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />

    <xsl:template match="/">
        <xsl:apply-templates mode="t1"/>
        <xsl:text>&#xa;</xsl:text>
        <xsl:apply-templates mode="t2"/>
    </xsl:template>

    <xsl:template match="*" mode="t1">
        <xsl:text>{</xsl:text>
        <xsl:value-of select="local-name()"/>
        <xsl:call-template name="strip-space">
            <xsl:with-param name="data">
                <xsl:apply-templates mode="t1"/>
            </xsl:with-param>
        </xsl:call-template>
        <xsl:text>}</xsl:text>
    </xsl:template>

    <xsl:template match="*" mode="t2">
        <xsl:text>{</xsl:text>
        <xsl:value-of select="local-name()"/>
        <xsl:value-of select="."/>
        <xsl:text>}</xsl:text>
    </xsl:template>

    <xsl:template name="strip-space">
        <xsl:param name="data"/>
        <xsl:value-of select="normalize-space($data)"/>
    </xsl:template>

</xsl:stylesheet>

Après avoir appliqué la feuille de style, il produit :

{test{my-element{e1some text} {e2some other text} {e3some other text}}}

{test

        some text some other text some other text

}

Le résultat décrit comment @mode="t1" ( <xsl:value-of select="."/> ) diffère de l'approche @mode="t2" ( xsl:call-template approche). J'espère que cela aidera quelqu'un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X