XSLT - 使用for-each-group对兄弟姐妹进行分组

时间:2015-09-16 06:39:20

标签: xml xslt xslt-2.0

我正在使用XSLT进行xml到html的转换,

我的输入xml文件如下所示,

<doc>
    <h1>main header 1-1</h1>
    <p>para 1</p>
    <p>para 2</p>

    <h1>main header 1-2</h1>
    <p>para 3</p>
    <p>para 4</p>
    <h2>sub header 2-1</h2>
    <p>para 5</p>
    <p>para 6</p>
    <p>para 7</p>
    <h2>sub header 2-3</h2>
    <p>para 8</p>
    <p>para 9</p>

    <h1>main header 1-3</h1>
    <p>para 10</p>
    <h2>sub header 2-3</h2>
    <p>para 11</p>
    <p>para 12</p>
</doc>

我在xml文件中的预期输出如下所示

   <doc>
        <div id="h1-1">
            <h1>main header 1-1</h1>
            <p>para 1</p>
            <p>para 2</p>
        </div>
        <div id="h1-2">
            <h1>main header 1-2</h1>
            <p>para 3</p>
            <p>para 4</p>
        </div>
        <div id="h2-1">
            <h2>sub header 2-1</h2>
            <p>para 5</p>
            <p>para 6</p>
            <p>para 7</p>
        </div>
        <div id="h2-2">
            <h2>sub header 2-3</h2>
            <p>para 8</p>
            <p>para 9</p>
        </div>
        <div id="h1-3">
            <h1>main header 1-3</h1>
            <p>para 10</p>
        </div>
        <div id="h2-3">
            <h2>sub header 2-3</h2>
            <p>para 11</p>
            <p>para 12</p>
        </div> 
   </doc>

我有跟随xsl来完成这项任务,

<xsl:function name="san:group-div" as="element()*">
        <xsl:param name="elements" as="element()*"/>
        <xsl:param name="level" as="xs:integer"/>
        <xsl:for-each-group select="$elements" group-starting-with="*[local-name() eq concat('h', $level)]">
            <xsl:choose>
                <xsl:when test="self::*[local-name() eq concat('h', $level)]">
                    <div id="{local-name()}-{count(preceding-sibling::*[local-name() eq local-name(current())]) + 1}"> 
                        <xsl:apply-templates select="current-group()"/>
                    </div>
                    <xsl:sequence select="san:group-div(current-group() except ., $level + 1)"/>
                </xsl:when>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:function>

    <xsl:template match="doc">
        <xsl:copy>
           <xsl:sequence select="san:group-div(*, 1)"/>
        </xsl:copy>
    </xsl:template>

然而它给了我以下结果。

<doc>
        <div id="h1-1">
            <h1>main header 1-1</h1>
            <p>para 1</p>
            <p>para 2</p>
        </div>
        <div id="h1-2">
            <h1>main header 1-2</h1>
            <p>para 3</p>
            <p>para 4</p>
            <h2>sub header 2-1</h2>
            <p>para 5</p>
            <p>para 6</p>
            <p>para 7</p>
            <h2>sub header 2-3</h2>
            <p>para 8</p>
            <p>para 9</p>
        </div>
        <div id="h2-1">
            <h2>sub header 2-1</h2>
            <p>para 5</p>
            <p>para 6</p>
            <p>para 7</p>
        </div>
        <div id="h2-2">
            <h2>sub header 2-3</h2>
            <p>para 8</p>
            <p>para 9</p>
        </div>
        <div id="h1-3">
            <h1>main header 1-3</h1>
            <p>para 10</p>
            <h2>sub header 2-3</h2>
            <p>para 11</p>
            <p>para 12</p>
        </div>
        <div id="h2-3">
            <h2>sub header 2-3</h2>
            <p>para 11</p>
            <p>para 12</p>
        </div>
  </doc>

如结果所示,<h2>个节点也会被复制到<h1> div。 (请参阅<div id="h1-2"><div id="h1-3">)。

任何人都可以建议我如何安排我的代码来获得预期的输出?

1 个答案:

答案 0 :(得分:2)

怎么样......

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

<xsl:output method="html" version="5.0" indent="yes" encoding="utf-8" />
<xsl:strip-space elements="*" />

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

<xsl:template match="doc">
  <doc>
    <xsl:for-each-group select="h1|h2|h3|p" group-adjacent="
      if (self::p)
        then
          (preceding-sibling::h1 | preceding-sibling::h2 | preceding-sibling::h3)[last()]/local-name()
        else
          local-name()">
      <xsl:for-each-group select="current-group()" group-starting-with="h1|h2|h3">
        <div id="{local-name()}-{position()}">   
          <xsl:copy-of select="current-group()" />      
        </div>    
      </xsl:for-each-group>   
    </xsl:for-each-group>
  </doc>
</xsl:template>

</xsl:stylesheet>

假设

这假设您的标题级别仅限于h1h2h3。如果没有,根据需要进行调整是一项微不足道的工作。