2015-08-13 27 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