36 votes

Conversion de XML en texte brut - comment dois-je ignorer/gérer les espaces blancs dans le XSLT ?

J'essaie de convertir un fichier XML dans le balisage utilisé par dokuwiki, en utilisant XSLT. Cela fonctionne dans une certaine mesure, mais l'indentation du fichier XSLT est insérée dans les résultats. Pour l'instant, j'ai deux choix : abandonner complètement ce XSLT et trouver un autre moyen de convertir le XML en balisage dokuwiki, ou supprimer environ 95 % de l'espace blanc du fichier XSL, ce qui le rend pratiquement illisible et constitue un cauchemar de maintenance.

Existe-t-il un moyen de conserver l'indentation dans le fichier XSL sans transmettre tous ces espaces blancs au document final ?

Contexte : Je suis en train de migrer un outil autodoc des pages HTML statiques vers dokuwiki, de sorte que l'API développée par l'équipe du serveur puisse être documentée par l'équipe des applications chaque fois que l'équipe des applications rencontre du code mal documenté. La logique est d'avoir une section de chaque page réservée à l'outil autodoc, et d'autoriser les commentaires en dehors de ce bloc. J'utilise XSLT parce que nous avons déjà le fichier XSL pour convertir de XML à XHTML, et je suppose qu'il sera plus rapide de réécrire le XSL que de développer ma propre solution à partir de zéro.

Edit : Ah, oui, je suis bête, j'ai négligé l'attribut indent. (Autre note de contexte : je suis nouveau en XSLT.) D'un autre côté, je dois encore m'occuper des nouvelles lignes. Dokuwiki utilise des pipes pour différencier les colonnes des tableaux, ce qui signifie que toutes les données d'une ligne de tableau doivent être sur une seule ligne. Existe-t-il un moyen de supprimer la sortie des nouvelles lignes (juste occasionnellement), afin que je puisse faire une logique assez complexe pour chaque cellule du tableau d'une manière un peu lisible ?

77voto

JeniT Points 3030

Il y a trois raisons pour obtenir des espaces blancs non désirés dans le résultat d'une transformation XSLT :

  1. les espaces blancs qui se trouvent entre les nœuds du document source
  2. les espaces blancs qui proviennent de l'intérieur des nœuds du document source
  3. les espaces blancs qui proviennent de la feuille de style

Je vais parler des trois car il peut être difficile de savoir d'où vient l'espace blanc et vous devrez peut-être utiliser plusieurs stratégies.

Pour traiter l'espace blanc qui se trouve entre les nœuds dans votre document source, vous devez utiliser la fonction <xsl:strip-space> pour supprimer les espaces qui apparaissent entre deux nœuds, puis utiliser la fonction <xsl:preserve-space> afin de préserver les espaces blancs importants qui pourraient apparaître dans un contenu mixte. Par exemple, si votre document source ressemble à :

<ul>
  <li>This is an <strong>important</strong> <em>point</em></li>
</ul>

alors vous voudrez ignorer l'espace entre les mots-clés <ul> y el <li> et entre les </li> y el </ul> ce qui n'est pas significatif, mais préservez l'espace entre les caractères d'imprimerie. <strong> y <em> éléments, qui es significatif (sinon vous obtiendriez "Ceci est une important* point*"). Pour ce faire, utilisez

<xsl:strip-space elements="*" />
<xsl:preserve-space elements="li" />

El elements l'attribut <xsl:preserve-space> doit essentiellement répertorier tous les éléments de votre document qui ont un contenu mixte.

A propos : l'utilisation <xsl:strip-space> réduit également la taille de l'arbre source en mémoire, et rend votre feuille de style plus efficace, donc cela vaut la peine de le faire même si vous n'avez pas de problèmes d'espaces blancs de ce genre.

Pour traiter les espaces blancs qui apparaissent à l'intérieur des nœuds de votre document source, vous devez utiliser la méthode suivante normalize-space() . Par exemple, si vous avez :

<dt>
  a definition
</dt>

et vous pouvez être sûr que le <dt> ne contient aucun élément avec lequel vous voulez faire quelque chose, alors vous pouvez le faire :

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

Les espaces en tête et en queue de page seront supprimés de la valeur de la balise <dt> et vous obtiendrez simplement la chaîne de caractères "a definition" .

En ce qui concerne les espaces blancs provenant de la feuille de style, ce qui est peut-être le cas pour vous, c'est lorsque vous avez du texte dans un modèle comme celui-ci :

<xsl:template match="name">
  Name:
  <xsl:value-of select="." />
</xsl:template>

Les feuilles de style XSLT sont analysées de la même manière que les documents sources qu'elles traitent, de sorte que le XSLT ci-dessus est interprété comme un arbre qui contient un fichier <xsl:template> avec un élément match dont le premier enfant est un nœud de texte et le second un nœud de type <xsl:value-of> avec un élément select attribut. Le nœud texte comporte des espaces en tête et en queue (y compris les sauts de ligne) ; comme il s'agit d'un texte littéral dans la feuille de style, il est littéralement copié dans le résultat, avec tous les espaces en tête et en queue.

Mais un peu de Les espaces blancs dans les feuilles de style XSLT sont automatiquement supprimés, notamment ceux entre les nœuds. Vous n'obtenez pas de saut de ligne dans votre résultat parce qu'il y a un saut de ligne entre l'élément <xsl:value-of> et la clôture de la <xsl:template> .

Pour obtenir uniquement le texte que vous voulez dans le résultat, utilisez la fonction <xsl:text> élément comme celui-ci :

<xsl:template match="name">
  <xsl:text>Name: </xsl:text>
  <xsl:value-of select="." />
</xsl:template>

Le processeur XSLT ne tiendra pas compte des sauts de ligne et de l'indentation qui apparaissent entre les nœuds, et n'affichera que le texte contenu dans l'élément <xsl:text> élément.

4voto

Lindsay Points 491

Utilisez-vous indent="no" dans votre balise de sortie ?

<xsl:output method="text" indent="no" />

De plus, si vous utilisez xsl:value-of, vous pouvez utiliser l'option disable-output-escaping="yes" pour résoudre certains problèmes d'espacement.

3voto

Dan Points 3171

La réponse de @JeniT est excellente, je veux juste signaler une astuce pour gérer les espaces blancs. Je ne suis pas certain que ce soit la meilleure façon (ou même une bonne façon), mais cela fonctionne pour moi pour le moment.

("s" pour espace, "e" pour vide, "n" pour nouvelle ligne).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:transform [
  <!ENTITY s "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" >
  <!ENTITY s2 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>  </xsl:text>" >
  <!ENTITY s4 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>    </xsl:text>" >
  <!ENTITY s6 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>      </xsl:text>" >
  <!ENTITY e "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'></xsl:text>" >
  <!ENTITY n "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
</xsl:text>" >
]>

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
  &e;Flush left, despite the indentation.&n;
  &e;  This line will be output indented two spaces.&n;

      <!-- the blank lines above/below won't be output -->

  <xsl:for-each select="//foo">
    &e;  Starts with two blanks: <xsl:value-of select="@bar"/>.&n;
    &e;  <xsl:value-of select="@baz"/> The 'e' trick won't work here.&n;
    &s2;<xsl:value-of select="@baz"/> Use s2 instead.&n;
    &s2;    <xsl:value-of select="@abc"/>    <xsl:value-of select="@xyz"/>&n;
    &s2;    <xsl:value-of select="@abc"/>&s;<xsl:value-of select="@xyz"/>&n;
  </xsl:for-each>
</xsl:template>
</xsl:transform>

S'applique à :

<?xml version="1.0" encoding="UTF-8"?>
<foo bar="bar" baz="baz" abc="abc" xyz="xyz"></foo>

Sorties :

Flush left, despite the indentation.
  This line will be output indented two spaces.
  Starts with two blanks: bar.
baz The 'e' trick won't work here.
  baz Use s2 instead.
  abcxyz
  abc xyz

L'astuce du 'e' fonctionne avant qu'un nœud de texte ne contienne au moins un caractère autre qu'un espace blanc, car il se développe comme ceci :

<xsl:template match="/">
  <xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text>
</xsl:text>

Depuis le règles pour supprimer les espaces blancs disent que les nœuds de texte contenant uniquement des espaces sont supprimés, la nouvelle ligne et l'indentation entre le <xsl:template> et le <xsl:text> sont supprimés (bon). Puisque les règles disent qu'un nœud de texte avec au moins un caractère d'espacement est préservé, le nœud de texte implicite contenant des caractères d'espacement est préservé. " This line will be output indented two spaces." conserve son espacement de tête (mais je suppose que cela dépend aussi des paramètres de dépouillement/préservation/normalisation). Le "&n ;" à la fin de la ligne insère une nouvelle ligne, mais il garantit également que tout espace blanc suivant est ignoré, car il apparaît entre deux noeuds.

Le problème que j'ai est lorsque je veux sortir une ligne indentée qui commence par un <xsl:value-of>. Dans ce cas, le "&e ;" ne sera d'aucune utilité, car l'espace blanc d'indentation n'est pas "attaché" à des caractères non blancs. Donc, pour ces cas, j'utilise "&s2 ;" ou "&s4 ;", selon le degré d'indentation que je souhaite.

Je suis sûr que c'est un hack moche, mais au moins je n'ai pas les verbeuses balises "<xsl:text>" qui jonchent mon XSLT, et au moins je peux encore indenter le XSLT lui-même pour qu'il soit lisible. J'ai l'impression d'abuser du XSLT pour quelque chose pour lequel il n'a pas été conçu (le traitement de texte) et c'est le mieux que je puisse faire.


Edit : En réponse aux commentaires, voici à quoi cela ressemble sans les "macros" :

<xsl:template match="/">
  <xsl:text>Flush left, despite the indentation.</xsl:text>
  <xsl:text>  This line will be output indented two spaces.</xsl:text>
  <xsl:for-each select="//foo">
    <xsl:text>  Starts with two blanks: </xsl:text><xsl:value-of select="@bar"/>.<xsl:text>
</xsl:text>
    <xsl:text>    </xsl:text><xsl:value-of select="@abc"/><xsl:text> </xsl:text><xsl:value-of select="@xyz"/><xsl:text>
</xsl:text>
  </xsl:for-each>
</xsl:template>

Je pense que cela rend moins claire l'indentation de la sortie prévue, et que cela perturbe l'indentation de la XSL elle-même parce que la balise </xsl:text> Les balises de fin doivent apparaître dans la colonne 1 du fichier XSL (sinon, vous obtenez des espaces non désirés dans le fichier de sortie).

0voto

Odilon Redo Points 191

En ce qui concerne votre édition sur les nouvelles lignes, vous pouvez utiliser ce modèle pour remplacer récursivement une chaîne dans une autre chaîne, et vous pouvez l'utiliser pour les sauts de ligne :

<xsl:template name="replace.string.section">
  <xsl:param name="in.string"/>
  <xsl:param name="in.characters"/>
  <xsl:param name="out.characters"/>
  <xsl:choose>
    <xsl:when test="contains($in.string,$in.characters)">
      <xsl:value-of select="concat(substring-before($in.string,$in.characters),$out.characters)"/>
      <xsl:call-template name="replace.string.section">
        <xsl:with-param name="in.string" select="substring-after($in.string,$in.characters)"/>
        <xsl:with-param name="in.characters" select="$in.characters"/>
        <xsl:with-param name="out.characters" select="$out.characters"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$in.string"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Appelez-le comme suit (cet exemple remplace les sauts de ligne dans la variable $some.string par un espace) :

    <xsl:call-template name="replace.string.section">
        <xsl:with-param name="in.string" select="$some.string"/>
        <xsl:with-param name="in.characters" select="'&#xA;'"/>
        <xsl:with-param name="out.characters" select="' '"/>
    </xsl:call-template>

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