Question

Question

Avec XSLT 1.0, étant donné une chaîne avec des caractères arbitraires, comment puis-je récupérer une chaîne qui respecte les règles suivantes:

  1. Le premier caractère doit être l'un des suivants: a-z, A-Z, deux points ou un trait de soulignement
  2. Tous les autres caractères doivent être des caractères supérieurs à 0 ou 9, un point ou un trait d'union
  3. Si un caractère ne respecte pas les règles ci-dessus, remplacez-le par un trait de soulignement

Arrière-plan

Dans un XSLT, je traduis certains attributs en éléments, mais je dois être sûr que l'attribut ne contient aucune valeur qui ne peut pas être utilisée dans un nom d'élément. Je me fiche de l'intégrité de l'attribut converti en nom tant qu'il est converti de manière prévisible. Je n'ai pas non plus besoin de compenser chaque caractère valide dans un nom d'élément (il y en a un tas).

Le problème que je rencontrais concernait les attributs comportant des espaces, que la fonction de traduction peut facilement convertir en caractères de soulignement:

translate(@name,' ','_')

Mais peu de temps après, j'ai trouvé certains attributs en utilisant des barres obliques, je dois donc les ajouter maintenant. Cela va vite devenir incontrôlable. Je veux pouvoir définir une liste blanche de caractères autorisés et remplacer tous les caractères non autorisés par un trait de soulignement, mais traduire les travaux comme en les remplaçant à partir d'une liste noire.

Était-ce utile?

La solution

Vous pouvez écrire un modèle récursif à cet effet, en analysant un à un les caractères de la chaîne, en les testant et en les modifiant si nécessaire. Quelque chose comme:

<xsl:template name="normalizeName">
  <xsl:param name="name" />
  <xsl:param name="isFirst" select="true()" />
  <xsl:if test="$name != ''">
    <xsl:variable name="first" select="substring($name, 1, 1)" />
    <xsl:variable name="rest" select="substring($name, 2)" />
    <xsl:choose>
      <xsl:when test="contains('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_', $first) or
                      (not($first) and contains('0123456789.-', $first))">
        <xsl:value-of select="$first" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>_</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:call-template name="normalizeName">
      <xsl:with-param name="name" select="$rest" />
      <xsl:with-param name="isFirst" select="false()" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Cependant, il existe un moyen plus simple de le faire si vous êtes prêt à faire du piratage. Commencez par déclarer certaines variables:

<xsl:variable name="underscores"
  select="'_______________________________________________________'" />
<xsl:variable name="initialNameChars"
  select="'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_'" />
<xsl:variable name="nameChars"
  select="concat($initialNameChars, '0123456789.-')" />

Maintenant, la technique consiste à prendre le nom et à identifier les caractères que ne sont pas légaux en remplaçant tous les caractères du nom qui sont légaux par rien. Vous pouvez le faire avec la fonction translate () . Une fois que vous avez défini le jeu de caractères non autorisés apparaissant dans la chaîne, vous pouvez les remplacer par des traits de soulignement en utilisant à nouveau la fonction translate () . Voici le modèle:

<xsl:template name="normalizeName">
  <xsl:param name="name" />
  <xsl:variable name="first" select="substring($name, 1, 1)" />
  <xsl:variable name="rest" select="substring($name, 2)" />
  <xsl:variable name="illegalFirst"
    select="translate($first, $initialNameChars, '')" />
  <xsl:variable name="illegalRest"
    select="translate($rest, $nameChars, '')" />
  <xsl:value-of select="concat(translate($first, $illegalFirst, $underscores),
                               translate($rest, $illegalRest, $underscores))" />
</xsl:template>

La seule chose à laquelle vous devez prêter attention est que la chaîne de soulignements doit être suffisamment longue pour couvrir tous les caractères illégaux pouvant figurer dans un même nom. Faire le même nom que le nom le plus long que vous rencontrerez probablement fera l'affaire (bien que vous puissiez probablement vous en tirer avec un nom beaucoup plus court).

Autres conseils

Pour autant que je sois conscient, XSLT 1.0 n’a pas de fonction intégrée pour cela. XSLT 2.0 vous permet de utiliser des regexes , même si Assurez-vous que vous êtes trop conscient de cela.

Si vous utilisez éventuellement l'analyseur MS, vous pouvez écrire des bibliothèques d'extension .NET que vous pouvez exploiter dans votre XSLT et j'ai écrit à ce sujet il y a quelques mois ici.

Si vous utilisez quelque chose comme Saxon, je suis à peu près certain qu'ils fournissent également des moyens de coder vos propres extensions, et ils peuvent en effet déjà avoir leur propre extension, mais je ne connais pas bien ce moteur.

J'espère que cela vous aidera.

Une autre possibilité consiste à utiliser une fonction de chaîne dans la bibliothèque standard XSLT. http://xsltsl.sourceforge.net/string.html#template. str: correspondance de chaîne

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top