2010-09-23 9 views
4

À partir de XSLT 2.0, pour autant que je sache (corrigez-moi si je me trompe), il n'y a pas de mécanisme natif dans le langage de gestion des exceptions.Techniques de gestion des exceptions XSLT

J'ai quelques feuilles de style qui tentent de faire un traitement sur des morceaux spécifiques d'un document d'entrée, en copiant tout le reste inchangé. Il y a des conditions exceptionnelles rares que je ne peux pas facilement détecter avant que je commence à produire la production pour un morceau donné. Celles-ci sont suffisamment rares pour que, lorsque je les rencontre, tout ce que je veux faire est d'annuler le traitement sur ce morceau et de l'émettre sans modification. Une sorte de gestion des exceptions est en ordre, mais XSLT n'aide pas beaucoup. Je ne veux pas avoir Java ou une autre langue dans le mix ici.

J'ai une solution réalisable décrite ci-dessous, mais je m'interroge sur d'autres approches. Avez-vous tous une meilleure façon de faire quelque chose comme ça?

Voici un exemple du genre de scénario dont je parle. Voici un document d'entrée:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 

Je souhaite parcourir chaque table et remplacer toutes les valeurs de cellule 'a' par 'B'. Cependant, s'il y a un 'x' quelque part dans la table, je veux juste copier la table non modifiée. Je sais que dans ce cas, je pourrais juste faire un test tr/td[.='x'] sur la table pour découvrir cette condition. Dans le cas réel, cependant, il n'est pas si facile de tester à l'avance pour la condition.

Voici quelques XSLT qui ne tient pas compte de l'exception:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match="table"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:apply-templates mode="inner"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template mode="inner" match="td"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:choose> 
       <xsl:when test=". = 'a'"> 
        <xsl:value-of select="'B'"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="."/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template mode="inner" match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates mode="inner" select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

La sortie de c'est:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
</doc> 

Il a fait les substitutions dans la deuxième table, que je ne vouloir.

Ma solution actuelle est de le faire:

  1. émettons chaque table dans une variable au lieu de directement dans la sortie
  2. Si l'exception se produit, émettent une étiquette <EXCEPTION/>
  3. Après chaque table est traitée , parcourez la variable pour la balise <EXCEPTION/>.
  4. Si l'exception s'est produite, copiez la table d'origine, sinon copiez le contenu de la variable.

Voici le code modifié:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match="table"> 
     <xsl:variable name="result"> 
      <xsl:copy> 
       <xsl:copy-of select="@*"/> 
       <xsl:apply-templates mode="inner"/> 
      </xsl:copy> 
     </xsl:variable> 
     <xsl:choose> 
      <xsl:when test="$result//EXCEPTION"> 
       <xsl:copy-of select="."/> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:copy-of select="$result"/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

    <xsl:template mode="inner" match="td"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:choose> 
       <xsl:when test=". = 'a'"> 
        <xsl:value-of select="'B'"/> 
       </xsl:when> 
       <xsl:when test=". = 'x'"> 
        <EXCEPTION/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="."/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template mode="inner" match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates mode="inner" select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@*|node()" priority="-10"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Et la sortie correcte:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 
+0

Ici, il n'y a pas d'exception. C'est la beauté du paradigme déclaratif sans effet secondaire. Toutes les fonctions qui peuvent tomber dans les effets secondaires ont un test aviable. Ici vous avez un processus différent pour les différents schémas d'éléments. Comme vous l'avez écrit, à chaque fois qu'il est possible d'utiliser la correspondance de motifs, faites-le. Il peut y avoir des cas où cela est difficile ou pas optimal. Ensuite, un processus en deux étapes sur les arbres de résultats temporaires est le chemin, comme votre exemple. –

+0

@ Steven-Ourada, @ Nick-Jones et @Alejandro: Bonne question (+1). Voir ma réponse pour une solution * très simple * qui n'a pas besoin d'un mécanisme try/catch :) Pour les cas * très complexes * où les exceptions peuvent vraiment être utiles, vous devez attendre XSLT 3.0. –

Répondre

1

Si vous ne pouvez attendre XSLT 2.1 il y aura alors essayer/expressions de capture

+1

Je crois qu'ils ont décidé que la prochaine version de XSLT (et XPATH et XQuery) sera "3.0" http://www.w3.org/Bugs/Public/show_bug.cgi?id=9715 –

+0

Nice. Je ne peux pas attendre, mais je pourrais passer à cela plus tard. Cela m'a aussi amené à regarder les extensions de Saxon (ce que j'aurais dû faire plus tôt). J'utilise la version HE, mais la version EE a un mécanisme similaire (http://www.saxonica.com/documentation/extensions/instructions/try-instr.html). Ne vaut pas la peine d'être amélioré, mais bon à savoir. –

1

L'exemple fourni est très simple et n'a pas besoin de capacités de gestion des exceptions.

Le mode attribut est votre ami:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
    <xsl:output omit-xml-declaration="yes"/> 

<xsl:template match="node()|@*" mode="#default copy"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*" mode="#current"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="table[tr/td[.='x']]"> 
    <xsl:apply-templates select="." mode="copy"/> 
</xsl:template> 

<xsl:template match="td/text()[.='a']">B</xsl:template> 
</xsl:stylesheet> 

lorsque cette transformation est appliquée sur le document XML fourni:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 

le désiré, résultat correct est produit:

<doc> 
    <block>some text, just copy.</block> 
    <!-- the following table should have B substituted for a --> 
    <table> 
     <tr><td>B</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>B</td><td>c</td></tr> 
     <tr><td>b</td><td>c</td><td>B</td></tr> 
    </table> 
    <block>some more text, just copy.</block> 
    <!-- the following table should be copied unaltered because of the presence of an x --> 
    <table> 
     <tr><td>a</td><td>b</td><td>c</td></tr> 
     <tr><td>b</td><td>a</td><td>x</td></tr> 
     <tr><td>b</td><td>c</td><td>a</td></tr> 
    </table> 
</doc> 

Dans certains cas exceptionnels de cas sont vraiment nécessaires (nous avons besoin de voir encore un bon cas d'utilisation), ils seront standard dans XSLT 3.0.

+1

Je suis sûr que vous avez remarqué que j'ai spécifiquement mentionné que mon cas de test ici était très simplifié. Désolé, je ne peux pas vous donner tout le cas réel; tout le détail obscurcirait de toute façon l'idée de base. Vous devez juste me faire confiance que je crois que des exceptions me seraient utiles :-). Parfois, la meilleure solution n'est pas la plus parfaite sur le plan conceptuel, mais plutôt la plus simple ... –

+0

@ Steven-Ourada: Oui, je * dois * te croire. Il vaudrait mieux être plus convaincant et donner un tel exemple. :) –