Conversion de XML en texte brut - Comment devrais-je ignorer / gérer les espaces dans la XSLT?

StackOverflow https://stackoverflow.com/questions/184431

  •  06-07-2019
  •  | 
  •  

Question

J'essaie de convertir un fichier XML dans le balisage utilisé par dokuwiki, à l'aide de XSLT. Cela fonctionne dans une certaine mesure, mais l'indentation dans le fichier XSL est insérée dans les résultats. Pour le moment, j'ai deux choix: abandonner entièrement cette chose XSLT et trouver un autre moyen de convertir XML en balise dokuwiki ou supprimer environ 95% de l'espace du fichier XSL, ce qui en fait un cauchemar de maintenance.

Existe-t-il un moyen de conserver l’indentation dans le fichier XSL sans laisser tout l’espace au document final?

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

Edit: Ah, d'accord, imbécile, j'ai négligé l'attribut indent. (Autre remarque: je suis nouveau dans XSLT.) Par contre, je dois encore gérer les nouvelles lignes. Dokuwiki utilise des tubes pour différencier les colonnes d'un tableau, ce qui signifie que toutes les données d'une ligne doivent être sur une seule ligne. Existe-t-il un moyen de supprimer les sauts de lignes (de temps en temps) afin que je puisse utiliser une logique assez complexe pour chaque cellule de tableau dans une fasion assez lisible?

Était-ce utile?

La solution

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

  1. les espaces entre les nœuds du document source
  2. les espaces provenant des nœuds du document source
  3. les espaces qui proviennent de la feuille de style

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

Pour traiter les espaces entre les nœuds de votre document source, vous devez utiliser <xsl:strip-space> pour supprimer tous les espaces apparaissant entre deux nœuds, puis <xsl:preserve-space> pour préserver les espaces significatifs pouvant 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 les espaces entre le <ul> et le <li> et entre le </li> et le </ul>, ce qui n'est pas significatif, mais conservez les espaces entre le <strong> et <= > éléments, ce qui est significatif (sinon, vous obtiendrez & "; Ceci est un ** point *** important * &";). Pour ce faire, utilisez

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

L'attribut <em> sur elements devrait répertorier tous les éléments de votre document ayant un contenu mixte.

  

De plus: l'utilisation de normalize-space() réduit également la taille de l'arborescence source en mémoire et améliore l'efficacité de votre feuille de style. Cela vaut donc la peine, même si vous n'avez pas de problèmes d'espaces de ce type.

Pour traiter les espaces apparaissant dans les nœuds de votre document source, utilisez <dt>. Par exemple, si vous avez:

<dt>
  a definition
</dt>

et vous pouvez être sûr que l'élément "a definition" ne contiendra 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 de début et de fin seront supprimés de la valeur de l'élément <xsl:template> et vous obtiendrez simplement la chaîne match.

Pour traiter les espaces provenant de la feuille de style, qui est peut-être celle que vous rencontrez, vous devez avoir 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 source qu’elles traitent. Le XSLT ci-dessus est donc interprété comme un arbre contenant un élément <xsl:value-of> avec un attribut select dont le premier enfant est un nœud de texte et dont le deuxième enfant est un élément <xsl:text> avec un attribut <=>. Le noeud de texte a des espaces de début et de fin (y compris les sauts de ligne); puisqu'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 de début et de fin.

Mais certains espaces dans les feuilles de style XSLT sont automatiquement supprimés, à savoir ceux entre les nœuds. Vous ne recevez pas de saut de ligne dans votre résultat car il y a un saut de ligne entre le <=> et la fin du <=>.

Pour obtenir uniquement le texte souhaité dans le résultat, utilisez l'élément <=> comme ceci:

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

Le processeur XSLT ignorera les sauts de ligne et l'indentation qui apparaissent entre les nœuds et ne produira que le texte contenu dans l'élément <=>.

Autres conseils

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

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

De même, si vous utilisez xsl: value-of, vous pouvez utiliser le disable-output-escaping = & "yes &"; pour aider avec des problèmes d'espaces blancs.

La réponse de @JeniT est excellente, je veux simplement signaler un truc pour gérer les espaces. Je ne suis pas sûr que ce soit le meilleur moyen (ou même un bon moyen), mais cela fonctionne pour moi pour le moment.

(& "; &"; s pour l'espace, & "e &" pour pour vide, & "n &" pour pour la 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>

Appliqué à:

<?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 "e" fonctionne avant un nœud de texte contenant au moins un caractère non blanc, car elle se développe comme suit:

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

Etant donné que les règles de suppression des espaces blancs indiquent que les nœuds de texte contenant uniquement des espaces sont supprimés, la nouvelle ligne et l'indentation entre le < xsl: template > et < xsl: text > se déshabiller (bien). Comme les règles indiquent qu'un nœud de texte avec au moins un caractère d'espacement est préservé, le nœud de texte implicite contenant " This line will be output indented two spaces." conserve son espace de tête (mais je suppose que cela dépend également des paramètres définis pour strip / preserve / normalize). Le & Quot; & Amp; n; & Quot; à la fin de la ligne insère une nouvelle ligne, mais garantit également que les espaces suivants sont ignorés, car ils apparaissent entre deux nœuds.

Le problème que j’ai, c’est que je veux sortir une ligne en retrait commençant par < xsl: value-of > ;. Dans ce cas, le & Quot; & Amp; e; & Quot; ne va pas aider, car le blanc d'indentation n'est pas & "attaché &"; à tous les caractères non-blancs. Donc, pour ces cas, j'utilise & Quot; & Amp; s2; & Quot; ou " & amp; s4; " ;, en fonction de l'indentation que je souhaite.

C’est un vilain bidouillage j'en suis sûr, mais au moins je n’ai pas le verbose & "; < xsl: text > &"; balises qui jonchent mon XSLT, et au moins je peux toujours indenter le XSLT lui-même pour qu'il soit lisible Je sens que j'abuse de XSLT pour quelque chose pour lequel il n'a pas été conçu (traitement de texte) et c'est tout ce que je peux faire.

Modifier: 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 la lecture de l'indentation de sortie prévue, et corrige l'indentation du fichier XSL lui-même, car les balises </xsl:text> end doivent apparaître dans la colonne 1 du fichier XSL (sinon, vous obtenez des espaces non désirés. dans le fichier de sortie).

En ce qui concerne vos modifications concernant les nouvelles lignes, vous pouvez utiliser ce modèle pour remplacer de manière récursive une chaîne dans une autre chaîne, ainsi que 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>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top