2015-08-13 70 views
0

目标是将以不同标题级别开始的元素分组为根据这些级别嵌套的部分。划分不同的标题级别

问题类似于XSLT: moving a grouping html elements into section levels。这里的区别在于标题级别并不严格。

举一个简单的例子,我想变换像

<body> 
    <p>0.1</p> 
    <p>0.2</p> 

    <h2>h2.1</h2> 
    <h3>h3.1</h3> 
    <p>3.1</p> 
    <p>3.2</p> 

    <h1>h1.1</h1> 
    <p>1.1</p> 
    <h3>h3.2</h3> 
    <p>3a.1</p> 
    <p>3a.2</p> 
</body> 

输入到这个所需的输出:

<document> 
    <body> 
     <p>0.1</p> 
     <p>0.2</p> 
     <section level="2"> 
     <h2>h2.1</h2> 
     <section level="3"> 
      <h3>h3.1</h3> 
      <p>3.1</p> 
      <p>3.2</p> 
     </section> 
     </section> 
     <section level="1"> 
     <h1>h1.1</h1> 
     <p>1.1</p> 
     <section level="3"> 
      <h3>h3.2</h3> 
      <p>3a.1</p> 
      <p>3a.2</p> 
     </section> 
     </section> 
    </body> 
</document> 

这是我到目前为止已经试过,使用一些修改在XSLT: moving a grouping html elements into section levels给出的解决方案:

<?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:mf="http://example.com/mf" 
    exclude-result-prefixes="xs mf" 
    version="2.0"> 

    <xsl:output indent="yes"/> 

    <xsl:template match="body"> 
     <document> 
      <xsl:copy> 
       <xsl:apply-templates select="@*"/> 
       <xsl:sequence select="mf:group(*, 1)"/> 
      </xsl:copy> 
     </document> 
    </xsl:template> 

    <xsl:template match="@* | node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*, node()"/> 
     </xsl:copy> 
    </xsl:template> 



    <xsl:function name="mf:group" as="node()*"> 
     <xsl:param name="elements" as="element()*"/> 
     <xsl:param name="level" as="xs:integer"/> 

     <xsl:for-each-group select="$elements" 
      group-starting-with="*[ 
       mf:isHead(local-name()) and 
       (mf:getHLevel(local-name()) = $level or 
        count(preceding::*[mf:isHead(local-name())]) = 0 
       ) 
       ]"> 
      <xsl:choose> 
       <xsl:when test="self::*[mf:getHLevel(local-name()) &lt; 999]"> 
        <xsl:variable name="myLevel" 
            select="mf:getHLevel(local-name())"/> 
        <section level="{$myLevel}"> 
         <xsl:copy> 
          <xsl:apply-templates select="@*, node()"/> 
         </xsl:copy> 
         <xsl:sequence 
          select="mf:group(current-group() except ., $myLevel + 1)"/> 
        </section> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:apply-templates select="current-group()"/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:for-each-group> 
    </xsl:function> 

    <!-- Functions: 
     mf:isHead(string): tests whether string is a headline-name (h1, h2,...) 
     mf:getHLevel(string): gets level of heading (h1 -> 1, h2 -> 2, ..., no heading -> 999) 
     --> 
    <xsl:function name="mf:getHLevel" as="xs:integer"> 
     <xsl:param name="s"/> 
     <xsl:value-of> 
      <xsl:choose> 
      <xsl:when test="mf:isHead($s)"> 
       <xsl:value-of select="xs:integer(replace($s,'.*?(\d+).*','$1'))"/>     
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="999"/> 
      </xsl:otherwise> 
      </xsl:choose> 
     </xsl:value-of> 
    </xsl:function> 

    <xsl:function name="mf:isHead" as="xs:boolean"> 
     <xsl:param name="s"/> 
     <xsl:value-of select="matches($s,'h\d+')"/> 
    </xsl:function> 
</xsl:stylesheet> 

我敢肯定,条件ns在@group-starting-with是错误的。即,count(preceding::*[mf:isHead(local-name())]) = 0似乎不检查,标题元素是否是当前元素序列中的第一个。但我无法弄清楚需要进行哪些修改才能达到理想的输出效果,所以我们不胜感激。

回答

1

我只想让功能组由当前水平,在最高水平停止(这是在HTML 6):

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

<xsl:function name="mf:group" as="node()*"> 
    <xsl:param name="nodes" as="node()*"/> 
    <xsl:param name="level" as="xs:integer"/> 
    <xsl:for-each-group select="$nodes" group-starting-with="*[starts-with(local-name(), concat('h', $level))]"> 
    <xsl:choose> 
     <xsl:when test="self::*[starts-with(local-name(), concat('h', $level))]"> 
     <section level="{$level}"> 
      <xsl:apply-templates select="."/> 
      <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/> 
     </section> 
     </xsl:when> 
     <xsl:when test="$level lt 6"> 
     <xsl:sequence select="mf:group(current-group(), $level + 1)"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:apply-templates select="current-group()"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:for-each-group> 
</xsl:function> 

<xsl:template match="@* | node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@* , node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="body"> 
    <xsl:copy> 
    <xsl:sequence select="mf:group(node(), 1)"/> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

显然,水平搜索可以作为一个参数,而不是提供在样式表中对其进行硬编码:

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

<xsl:param name="max-level" as="xs:integer" select="6"/> 

<xsl:param name="name-prefix" as="xs:string" select="'h'"/> 

<xsl:output method="html" indent="yes"/> 

<xsl:function name="mf:group" as="node()*"> 
    <xsl:param name="nodes" as="node()*"/> 
    <xsl:param name="level" as="xs:integer"/> 
    <xsl:for-each-group select="$nodes" group-starting-with="*[starts-with(local-name(), concat($name-prefix, $level))]"> 
    <xsl:choose> 
     <xsl:when test="self::*[starts-with(local-name(), concat($name-prefix, $level))]"> 
     <section level="{$level}"> 
      <xsl:apply-templates select="."/> 
      <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/> 
     </section> 
     </xsl:when> 
     <xsl:when test="$level lt $max-level"> 
     <xsl:sequence select="mf:group(current-group(), $level + 1)"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:apply-templates select="current-group()"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:for-each-group> 
</xsl:function> 

<xsl:template match="@* | node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@* , node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="body"> 
    <xsl:copy> 
    <xsl:sequence select="mf:group(*, 1)"/> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 
+0

我已经在您的真实问题中成功地使用了您的建议。这非常有帮助,谢谢。 – leu