XSLT:棘手的转型,建议?

时间:2009-08-23 10:47:58

标签: xml xslt xpath

我必须构建一个XSLT样式表来转换像这样的xml:

<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元素的关闭方式

我怎么能这样做?

3 个答案:

答案 0 :(得分: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>

答案 1 :(得分: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>

答案 2 :(得分: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(如果你看到的话)我的意思是说)。如果您想要重构版本来表明这一点,请告诉我......