2009-08-23 40 views
3

我必須建立一個XSLT樣式錶轉換這樣一個xml:XSLT:棘手的轉變,建議?

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

弄成這個樣子:

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

(注意每個word元素如何被一個silence元素後關閉)

我該怎麼做?

+0

我認爲你的例子中的第三個單詞元素的代碼值應該是「。」,而不是「 - 」。 – 2009-08-23 12:13:45

+0

你是對的,謝謝! – asymmetric 2009-08-24 11:33:55

回答

3

該解決方案既:略短,更重要的是,更高效由於使用鍵的:

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

當這個變換所提供的源XML應用,正確的結果產生

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

傳統上,它是摩爾斯電碼中的「點」和「短劃線」(不是「行」),除非我錯了。但那是微不足道的。 :-) +1從我,優越的解決方案。 – Tomalak 2009-08-24 10:14:43

+0

哦,但我看到OP正在使用「line」。所以沒關係。 ;) – Tomalak 2009-08-24 13:30:57

1

如何:

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

對於大文件,它可能會引發無窮遞歸檢測 - 在這種情況下,我想嘗試一下涉及<xsl:apply-templates select="*[1] | silence"/>的東西,並在每個情況下,只有往前走,只要下一個沉默或EOF(如果你明白我的意思)。讓我知道如果你想有一個重構的版本顯示此...

2

我相信,這達到你在找什麼:

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

輸出元素名稱中的一些微不足道的小故障,但非常光滑的方法;不錯。 – 2009-08-23 12:31:43

+0

哎呀!謝謝,Marc。我更新了樣式表以生成正確的元素/屬性名稱。此外,將第一個源元素的generate-id評估移到變量中,並從靜默模板中移出以提高效率。 – 2009-08-23 12:46:05