J'utilise XML pour stocker une petite liste de contacts et j'essaie d'écrire un modèle XSL qui le transformera en un fichier CSV. Le problème que j'ai est avec les espaces blancs dans la sortie.
Le résultat:
Friend, John, Smith, Home,
123 test,
Sebastopol,
California,
12345,
Home 1-800-123-4567, Personal [email protected]
J'ai mis en retrait/espacé à la fois le fichier XML source et le modèle XSL associé pour faciliter la lecture et le développement, mais tout cet espace blanc supplémentaire est en train de se retrouver dans la sortie. Le XML lui-même n'a pas d'espace supplémentaire à l'intérieur des nœuds, juste à l'extérieur pour la mise en forme, et il en va de même pour le XSLT.
Pour que le fichier CSV soit valide, chaque entrée doit se trouver sur sa propre ligne, et non fractionnée. En plus de supprimer tous les espaces blancs supplémentaires de XML et de XSLT (en les transformant en une longue ligne de code), existe-t-il un autre moyen de supprimer les espaces blancs dans la sortie?
Edit: Voici un petit exemple 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>[email protected]</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> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Dans XSLT, l’espace blanc est préservé par défaut, car il peut très bien s'agir de données pertinentes.
Le meilleur moyen d'éviter les espaces vides dans la sortie est de ne pas le créer. Ne fais pas:
<xsl:template match="foo">
foo
</xsl:template>
parce que c'est "\n··foo\n"
, du point de vue du processeur. Plutôt faire
<xsl:template match="foo">
<xsl:text>foo</xsl:text>
</xsl:template>
Les espaces dans la feuille de style sont ignorés tant qu'ils se produisent entre des éléments XML uniquement. En termes simples: n'utilisez jamais de texte "nu" dans votre code XSLT, mais insérez-le toujours dans un élément.
En outre, en utilisant un non spécifique:
<xsl:apply-templates />
est problématique, car la règle XSLT par défaut pour les nœuds de texte dit "les copier dans la sortie". Ceci s'applique également aux nœuds "d'espaces blancs". Par exemple:
<xml>
<data> value </data>
</xml>
contient trois nœuds de texte:
"\n··"
(juste après <xml>
)"·value·"
\n"
(juste avant </xml>
)Pour éviter que les n ° 1 et n ° 3 se faufilent dans la sortie (ce qui est la raison la plus courante pour les espaces indésirables), 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 maintenant coupés et la sortie de texte doit être créée explicitement:
<xsl:value-of select="data" />
Pour supprimer les espaces d'une valeur, vous pouvez utiliser la fonction normalize-space()
XSLT:
<xsl:value-of select="normalize-space(data)" />
Mais attention, puisque la fonction normalise tout espace blanc trouvé dans la chaîne, par ex. "·value··1·"
deviendrait "value·1"
.
De plus, vous pouvez utiliser les éléments <xsl:strip-space>
et <xsl:preserve-space>
, bien que ce ne soit généralement pas nécessaire (et personnellement, je préfère la gestion explicite des espaces, comme indiqué ci-dessus).
Par défaut, les modèles XSLT ont <xsl:preserve-space>
défini, ce qui permet de conserver des espaces dans votre sortie. Vous pouvez ajouter <xsl:strip-space elements="*">
pour indiquer à Où supprimer les espaces.
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 de pour préserver/décaper de W3 Schools .
En ce qui concerne la suppression des onglets mais la conservation des lignes séparées, j'ai essayé l'approche XSLT 1.0 suivante, et cela fonctionne plutôt bien. Votre utilisation de la version 1.0 ou 2.0 dépend en grande partie de la plate-forme que vous utilisez. Il semble que la technologie .NET soit toujours dépendante de XSLT 1.0 et vous êtes donc limité aux modèles extrêmement compliqués (voir ci-dessous). Si vous utilisez Java ou autre chose, veuillez vous référer à l’approche beaucoup plus propre de XSLT 2.0 énumérée tout en bas.
Ces exemples sont destinés à être étendus par vous pour répondre à vos besoins spécifiques. J'utilise les onglets ici à titre d'exemple, mais cela devrait être assez 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 (requis 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="'	'" />
<xsl:with-param name="replace-string" select="''" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 rend ceci trivial avec la fonction replace
:
<?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(), '	', '')" />
</xsl:template>
</xsl:stylesheet>
D'autres ont déjà signalé le problème général. Un spécifique pour votre feuille de style est que vous avez oublié <xsl:text>
pour les virgules:
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" />,
</xsl:when>
<xsl:otherwise>Home </xsl:otherwise>
</xsl:choose>
<xsl:value-of select="STREET" />,
<xsl:value-of select="CITY" />,
<xsl:value-of select="STATE" />,
<xsl:value-of select="Zip" />,
Cela rend les espaces après chaque virgule significatifs et finit par apparaître dans la sortie. Si vous placez chaque virgule dans <xsl:text>
, le problème disparaît.
En outre, débarrassez-vous de ce disable-output-escaping
. Cela ne fait rien ici, puisque vous ne sortez pas du XML.
Ma réponse précédente est fausse, toutes les virgules doivent être entrées avec 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> </xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()|@*">
<xsl:text>-</xsl:text>
</xsl:template>
</xsl:stylesheet>
Ajouter un modèle dans votre xslt
<xsl:template match="text()"/>
Modifier le code que nous avons utilisé pour formater le fichier XML brut en supprimant les lignes ci-dessous supprimera les espaces vides supplémentaires ajoutés dans Excel exporté.
Lors du formatage avec le système de propriétés en retrait, ces espaces sont vides.
Commentez les lignes liées au formatage XML comme ci-dessous et essayez.
xmlWriter.Formatting = System.Xml.Formatting.Indented;