2009-08-23 9 views
3

je dois construire une feuille de style XSLT qui transforme un fichier XML comme ceci:XSLT: transformation délicate, suggestions?

<message> 
<line/> 
<silence/> 
<dot/><line/><line/> 
<silence/> 
<dot/> 
<silence/> 
<line/><dot/><dot/><dot/> 
</message> 

en quelque chose comme ceci:

<complexMessage> 
<word code="-"/> 
<word code=".--"/> 
<word code="."/> 
<word code="-..."/> 
</complexMessage> 

(remarquez comment chaque élément word se ferme après un élément silence)

comment pourrais-je faire cela?

+0

Je pense que le troisième élément de mot dans votre exemple devrait avoir une valeur de code ".", Pas "-". –

+0

vous avez raison, merci! – asymmetric

Répondre

3

Cette solution est à la fois un peu plus courte et, plus important encore, plus efficace en raison de l'utilisation de clés:

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

    <xsl:key name="kPhraseByMorse" 
      match="*[not(self::silence) and not(self::message)]" 
      use="generate-id(preceding-sibling::silence[1])"/> 

    <xsl:template match="/"> 
     <complexMessage> 
     <word> 
      <xsl:call-template name="makeCode"/> 
     </word> 

     <xsl:apply-templates select="*/silence"/> 
     </complexMessage> 
    </xsl:template> 

    <xsl:template match="silence"> 
     <word> 
    <xsl:call-template name="makeCode"> 
     <xsl:with-param name="pId" select="generate-id()"/> 
    </xsl:call-template> 
     </word> 
    </xsl:template> 

    <xsl:template name="makeCode"> 
     <xsl:param name="pId"/> 
     <xsl:attribute name="code"> 
     <xsl:apply-templates select="key('kPhraseByMorse', $pId)"/> 
     </xsl:attribute> 
    </xsl:template> 

    <xsl:template match="dot">.</xsl:template> 
    <xsl:template match="line">-</xsl:template> 
</xsl:stylesheet> 

Lorsque cette transformation est appliquée sur le XML source fourni, le résultat correct est produit:

<complexMessage> 
    <word code="-"/> 
    <word code=".--"/> 
    <word code="."/> 
    <word code="-..."/> 
</complexMessage> 
+0

Traditionnellement, c'est "point" et "tiret" (pas "ligne") en code Morse, à moins que je me trompe. Mais c'est mineur. :-) +1 de moi, solution supérieure. – Tomalak

+0

Oh, mais je vois que l'OP utilise lui-même "line". Alors peu importe. ;) – Tomalak

1

Que diriez-vous:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:template match="/message"> 
     <complexMessage> 
     <xsl:apply-templates select="*[1]"/> 
     </complexMessage> 
    </xsl:template> 
    <xsl:template match="line" mode="value">-</xsl:template> 
    <xsl:template match="dot" mode="value">.</xsl:template> 
    <xsl:template match="silence" mode="value"/> 

    <xsl:template match="silence" name="write"> 
    <xsl:param name="prev" select="''"/> 
    <word code="{$prev}"/> 
    <xsl:apply-templates select="following-sibling::*[1]"/> 
    </xsl:template> 
    <xsl:template match="line | dot"> 
    <xsl:param name="prev" select="''"/> 
    <xsl:variable name="value"><xsl:apply-templates select="." mode="value"/></xsl:variable> 
    <xsl:choose> 
     <xsl:when test="following-sibling::*"> 
     <xsl:apply-templates select="following-sibling::*[1]"> 
      <xsl:with-param name="prev" select="concat($prev,$value)"/> 
     </xsl:apply-templates> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:call-template name="write"> 
      <xsl:with-param name="prev" select="concat($prev,$value)"/> 
     </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

Pour les grands documents, il pourrait déclencher la détection d'une récursion infinie - dans ce cas, je vais essayer de regarder quelque chose qui implique <xsl:apply-templates select="*[1] | silence"/>, et aller que dans chaque cas avant jusqu'à la prochaine silence ou EOF (si vous voyez ce que je veux dire). Faites-moi savoir si vous voulez une version remaniée pour montrer cette ...

2

Je crois que cette mesure permet ce que vous cherchez:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:variable name="firstSilenceID" select="generate-id(//silence[1])" /> 
    <xsl:template match="/"> 
     <complexMessage> 
      <xsl:apply-templates select="/message//silence"/> 
     </complexMessage> 
    </xsl:template> 

    <xsl:template name="message"> 

    </xsl:template> 

    <xsl:template match="silence" > 
     <!--If this is the first <silince> element, generate words for everything before it --> 
     <xsl:if test="$firstSilenceID = generate-id(current())"> 
      <xsl:call-template name="complexMessage"> 
       <xsl:with-param name="word" select="preceding-sibling::*"/> 
      </xsl:call-template> 
     </xsl:if> 
     <!--Generate words for everything after THIS <silence> element --> 
     <xsl:call-template name="complexMessage"> 
      <xsl:with-param name="word" select="following-sibling::*[generate-id(preceding-sibling::silence[1]) = generate-id(current())]"/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="complexMessage"> 
     <xsl:param name="word"/> 
      <word> 
       <xsl:attribute name="code"> 
        <xsl:apply-templates select="$word" /> 
       </xsl:attribute> 
      </word> 
    </xsl:template> 

    <xsl:template match="dot"> 
     <xsl:text>.</xsl:text> 
    </xsl:template> 

    <xsl:template match="line"> 
     <xsl:text>-</xsl:text> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Quelques pépins triviaux dans les noms des éléments de sortie, mais une approche très lisse; agréable. –

+0

Oups! Merci, Marc. J'ai mis à jour la feuille de style pour produire les noms d'éléments/attributs corrects. Aussi, déplacé l'évaluation de l'identifiant de génération pour le premier élément source dans une variable et hors du gabarit de silence pour une meilleure efficacité. –