XSLT: хитрое преобразование, предложения? - PullRequest
3 голосов
/ 23 августа 2009

Мне нужно создать таблицу стилей 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 ]

3 голосов
/ 24 августа 2009

Это решение одновременно несколько короче и, что более важно, более эффективно благодаря использованию клавиш :

<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>
2 голосов
/ 23 августа 2009

Я считаю, что это то, что вы ищете:

    <?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>
1 голос
/ 23 августа 2009

Как насчет:

<?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 (если вы видите имею в виду). Дайте мне знать, если вы хотите, чтобы рефакторированная версия показала это ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...