如何正确使用xsl:group-start-with和以下兄弟

时间:2015-09-14 13:40:58

标签: xml xslt xslt-2.0

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

我的要求是从<h1>创建无序列表,如果<h2>被放置为<h1>的兄弟姐妹,则应列在<h1>列表中。然后将这些列表项链接到<p>个节点之后。

SO,我的预期输出,

<doc>
    <div name="list">
        <ul id="menu">
            <li class='has-sub'><a href="h1-1">main header 1-1</a></li>
            <li class='has-sub'><a href="h1-2">main header 1-2</a>
                <ul>
                    <li class='last'><a href="h2-1">sub header 2-1</a></li>
                    <li class='last'><a href="h2-2">sub header 2-2</a></li>
                </ul>
            </li>
            <li class='has-sub'><a href="h1-3">main header 1-3</a>
                <ul>
                    <li class='last'><a href="h2-3">sub header 2-3</a></li>
                </ul>
            </li>
        </ul>
    </div>
    <div class="main">

        <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 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>
        <div id="h1-3">
            <h1>main header 1-3</h1>
            <p>para 10</p>
            <div id="h2-3">
                <h2>sub header 2-3</h2>
                <p>para 11</p>
                <p>para 12</p>
            </div>
        </div>
    </div>
</doc>

我写的xsl是为了得到这个输出,

<xsl:template match="doc">
        <doc>
            <div name="list">
                <ul>
                    <xsl:for-each-group select="*" group-starting-with="h1">
                        <li><a href="h1-{count(preceding::h1)+1}"><xsl:apply-templates/></a> 
                            <xsl:variable name="h2" select="current-group()[self::h2]"/>
                            <xsl:if test="$h2">
                                <ul>
                                    <xsl:apply-templates select="$h2" mode="san"/>
                                </ul>
                            </xsl:if>
                        </li>
                    </xsl:for-each-group>
                </ul>
            </div>

            <div class="main">
                <xsl:for-each-group select="*" group-starting-with="h2">  
                    <div id="h2-{count(preceding::h2)+1}">
                        <xsl:apply-templates select="current-group()[self::*]"/>
                    </div>
                </xsl:for-each-group>

                <xsl:for-each-group select="*" group-starting-with="h1">  
                    <div id="h1-{count(preceding::h1)+1}">
                        <xsl:apply-templates select="current-group()[self::*]"/>
                    </div>
                </xsl:for-each-group>
            </div>
        </doc>
    </xsl:template>

    <xsl:template match="h2" mode="san">
        <li><a href="h2-{count(preceding::h2)+1}"><xsl:apply-templates/></a></li>
    </xsl:template> 

第一部分工作正常,但我遇到的问题是结果xml我加倍了内容,<div> s内的id显示错误。

我得到了结果,

<doc>
    <div name="list">
        <ul>
            <li><a href="h1-1">main header 1-1</a></li>
            <li><a href="h1-2">main header 1-2</a>
                <ul>
                    <li><a href="h2-1">sub header 2-1</a>
                    </li>
                    <li><a href="h2-2">sub header 2-3</a></li>
                </ul>
            </li>
            <li>
                <a href="h1-3">main header 1-3</a>
                <ul>
                    <li><a href="h2-3">sub header 2-3</a></li>
                </ul>
            </li>
        </ul>
    </div>
    <div class="main">
        <div id="h2-1">
            <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>
        </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>
            <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>
        <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="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>
</doc>

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

1 个答案:

答案 0 :(得分:2)

I would use two functions to handle the two different grouping requirements:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.org/mf"
  exclude-result-prefixes="xs mf"
  version="2.0">

<xsl:output indent="yes"/>

<xsl:function name="mf:group-list" as="element()*">
  <xsl:param name="elements" as="element()*"/>
  <xsl:param name="level" as="xs:integer"/>
  <xsl:if test="$elements[self::*[local-name() eq concat('h', $level)]]">
    <ul>
      <xsl:for-each-group select="$elements" group-starting-with="*[local-name() eq concat('h', $level)]">
        <li>
          <a href="#{local-name()}-{count(preceding-sibling::*[local-name() eq local-name(current())]) + 1}">
            <xsl:value-of select="."/>
          </a>
          <xsl:sequence select="mf:group-list(current-group() except ., $level + 1)"/>
        </li>
      </xsl:for-each-group>
    </ul>
  </xsl:if>
</xsl:function>

<xsl:function name="mf: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:copy-of select="."/>
          <xsl:sequence select="mf:group-div(current-group() except ., $level + 1)"/>
        </div>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="current-group()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each-group>
</xsl:function>

<xsl:template match="doc">
  <xsl:copy>
    <div name="list">
      <xsl:sequence select="mf:group-list(h1 | h2 | h3 | h4 | h5 | h6, 1)"/>
    </div>
    <div class="main">
      <xsl:sequence select="mf:group-div(*, 1)"/>
    </div>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>