2009-08-23 6 views
3

ich habe ein Stylesheet erstellen, die eine XML wie folgt transformiert:XSLT: knifflige Transformation, Vorschläge?

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

in etwa wie folgt:

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

(beachten Sie, wie jedes word Element nach einem silence Element geschlossen wird)

Wie könnte ich das tun?

+0

Ich denke, das dritte Wortelement in Ihrem Beispiel sollte einen Codewert von "." Haben, nicht "-". –

+0

du hast Recht, danke! – asymmetric

Antwort

3

Diese Lösung ist beides: etwas kürzer und, was noch wichtiger ist, effizient mehr aufgrund der Verwendung von Schlüsseln:

<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> 

Wenn diese Transformation auf dem mitgelieferten XML-Quell angelegt wird, wird das richtige Ergebnis erzeugt:

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

Traditionell ist es "Punkt" und "Strich" (nicht "Linie") in Morse-Code, es sei denn, ich täusche mich. Aber das ist unbedeutend. :-) +1 von mir, überlegene Lösung. – Tomalak

+0

Oh, aber ich sehe das OP verwendet "Linie" selbst. Also nichts dagegen. ;) – Tomalak

1

Wie wäre:

<?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> 

Für große Dokumente könnte es eine unendliche Rekursion Erkennung auslösen - in diesem Fall würde ich versuchen, etwas zu betrachten <xsl:apply-templates select="*[1] | silence"/> beteiligt und jeweils nur vorwärts gehen, bis zum nächsten Schweigen oder EOF (wenn Sie sehen, was ich meine). Lassen Sie mich wissen, wenn Sie eine Überarbeitete Version dieses zeigen wollen ...

2

Ich glaube, dass dies erreicht, was Sie suchen:

<?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

Einige triviale Fehler in den Ausgabeelementnamen, aber ein sehr glatter Ansatz; nett. –

+0

Whoops! Danke, Marc. Ich habe das Stylesheet aktualisiert, um die korrekten Element-/Attributnamen zu erstellen. Außerdem wurde die Auswertung der Generate-ID für das erste Quellelement in eine Variable und aus der Silence-Vorlage zur besseren Effizienz verschoben. –

Verwandte Themen