XSLT用匹配规则替换条件逻辑

时间:2016-11-03 17:12:35

标签: xml xslt

我有一个XSLT样式表,它以错误的顺序调用模板。它不是按照它们出现的顺序呈现段落和表格,而是呈现所有段落,然后是所有表格。我猜这个问题可以通过用模式匹配替换条件逻辑来解决,但是我很难搞清楚如何做到这一点。

完整的XSLT在这里:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output indent="yes" method="html"/>

   <xsl:template match="/*">
        <html>
            <head></head>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="richtext">
        <xsl:if test="par">
            <xsl:apply-templates select="." mode="par" />
        </xsl:if>
        <xsl:if test="table">
            <xsl:apply-templates select="." mode="table" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="richtext" mode="par">
        <xsl:for-each-group select="par[run[normalize-space()]]" group-adjacent="if (@def) then @def else preceding-sibling::par[run[normalize-space()]][@def][1]/@def">
            <xsl:variable name="listType" select="preceding-sibling::*[1][self::pardef]/@list" />
            <xsl:choose>
                <xsl:when test="$listType = 'bullet'">    
                    <ul>
                        <xsl:apply-templates select="current-group()" mode="list"/>
                    </ul>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()" mode="para" />   
                </xsl:otherwise>     
            </xsl:choose>   
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="richtext" mode="table">
        <table border="1">
            <xsl:for-each select="table/tablerow">
                <tr>
                    <xsl:for-each select="tablecell">
                        <td>
                            <xsl:apply-templates />
                        </td>
                    </xsl:for-each>
                </tr>
            </xsl:for-each>
        </table>  
    </xsl:template>

    <xsl:template match="par" mode="list">
        <li>
            <xsl:apply-templates select="run" />
        </li>  
    </xsl:template>

    <xsl:template match="par" mode="para">
        <p>
            <xsl:apply-templates select="run" />
        </p>  
    </xsl:template>

    <xsl:template match="run">
         <xsl:value-of select="text()" separator=""/>
    </xsl:template>

</xsl:stylesheet>

这是XML:

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <item name="Some richtext">
        <richtext>
            <pardef/>
            <par def="20">
                <run>This is a </run>
                <run>paragraph.</run>
            </par>
            <table>
                <tablerow>
                    <tablecell>
                        <par def="43"><run>This is a table</run></par></tablecell>
                    <tablecell>
                        <par def="44"><run>This is some data</run></par></tablecell>
                </tablerow>
            </table>
            <pardef id="21" list="bullet"/>
            <par def="21">
                <run>This is a </run>
                <run>bullet point.</run>
            </par>
            <table>
                <tablerow>
                    <tablecell>
                        <par def="43"><run>This is another table</run></par></tablecell>
                    <tablecell>
                        <par def="44"><run>This is some data</run></par></tablecell>
                </tablerow>
            </table>
        </richtext>
    </item>
</document>

这是我想要的输出:

<html>
   <head></head>
  <body>
     <p>This is a paragraph.</p>
     <table border="1">
        <tr>
           <td>This is a table</td>
           <td>This is some data</td>
        </tr>
     </table>
     <ul>
        <li>This is a bullet point.</li>
     </ul>
     <table border="1">
           <tr>
              <td>This is another table</td>
              <td>This is some data</td>
           </tr>
     </table>
  </body>
</html>

1 个答案:

答案 0 :(得分:2)

看起来很多问题源于尝试使用@mode来完成@match的工作。一个好的经验法则是避免使用@mode,除非您可能多次处理相同的节点或节点类型,或者想要在不同的时间执行不同的操作。

正如@ michael.hor257k所说,你最好让应用模板为你做逻辑选择。

此外,您的测试有点不稳定,希望看看是否有富文本的表/ par子,而不是当前上下文节点(这是apply-templates已经为您做的):xsl:if对你的例子来说,这些陈述都是正确的。

以下是一些XSLT,它将根据您的输入创建您想要的内容(使用XHTML而不是HTML):我最终使用模式来区分列表和正常段落,但这符合以上经验法则:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:local="http://example.com/local" 
  exclude-result-prefixes="xs local"
  version="2.0">

  <xsl:output indent="yes" method="xhtml"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="pars" match="par" use="preceding-sibling::pardef[1]/generate-id()"/>

  <xsl:template match="document">
    <html>
      <head/>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <!-- I want to handle par using modes, so I deliberately remove any output in the default mode.  We'll make an exception for par in table later. -->
  <xsl:template match="par"/>

  <xsl:template match="pardef[not(@list)]">
    <!-- I'm using a key() function here just to easily return the par elements that follow this pardef
      - see the xsl:key definition above.  You could also try using @id and @def e.g. 
      <key name="pars" match="par" use="@def"/>
      and key('pars', @id) - although the first @id in your example seems to be missing!
    -->

    <xsl:apply-templates select="key('pars', generate-id())" mode="p"/>
  </xsl:template>

  <xsl:template match="pardef[@list]">
    <ul>
      <xsl:apply-templates select="key('pars', generate-id())" mode="li"/>
    </ul>
  </xsl:template>

  <xsl:template match="par" mode="p">
    <p>
      <!-- Any further elements don't need mode handling, so we'll return to the default mode -->
      <xsl:apply-templates mode="#default"/>
    </p>
  </xsl:template>

  <xsl:template match="par" mode="li">
    <li>
      <xsl:apply-templates mode="#default"/>
    </li>
  </xsl:template>

  <xsl:template match="table">
    <table border="1">
      <xsl:apply-templates/>
    </table>
  </xsl:template>

  <xsl:template match="tablerow">
    <tr>
      <xsl:apply-templates/>
    </tr>
  </xsl:template>

  <xsl:template match="tablecell/par">
    <td>
      <xsl:apply-templates/>
    </td>
  </xsl:template>

</xsl:stylesheet>

如果有人能告诉我为什么我必须使用generate-id()而不仅仅是。在关键功能和定义中,我想知道 - 我认为这可能是我的Saxon(9.6.0.7)中的一个错误。