2013-04-24 12 views
0

這是一個跟在"Select all of an element between the current element and the next of the current element"之間的問題。即使如果我不確定創建一個新問題是否正確,我仍然會這樣做。因爲原來的問題已經回答,但後來改變了。所以我認爲改變的問題是開放的。此外,我認爲如果這個問題適合答案,應該把這個改變後的問題放回到統計數據中。創建一個分層xml表單「flat」xml,類似於圖書的說明

問題是如何創建一個像書籍描述一樣的「flat」xml的分層xml格式。

輸入XML是一樣的東西

<root> 
    <heading_1>Section 1</heading_1> 
    <para>...</para> 
    <list_1>...</list_1> 
    <heading_2>Section 1.1</heading_2> 
    <para>...</para> 
    <heading_3>Section 1.1.1</heading_3> 
    <para>...</para> 
    <list_1>...</list_1> 
    <heading_2>Section 1.2</heading_2> 
    <para>...</para> 
    <footnote>...</footnote> 
    <heading_1>Section 2</heading_1> 
    <para>...</para> 
    <list_1>...</list_1> 
    <heading_2>Section 2.1</heading_2> 
    <para>...</para> 
    <list_1>...</list_1> 
    <list_2>...</list_2> 
    <heading_3>Seciton 2.1.1</heading_3> 
    <para>...</para> 
    <heading_2>Section 2.2</heading_2> 
    <para>...</para> 
    <footnote>...</footnote> 
</root> 

每個<heading_*>應該被解釋爲一個<section> 預期輸出XML是開始。

<section> 
    <title>Section 1</title> 
    <para>...</para> 
    <list_1>...</list_1> 
    <section> 
     <title>Section 1.1</title> 
     <para>...</para> 
     <section> 
      <title>Section 1.1.1</title> 
      <para>...</para> 
      <list_1>...</list_1> 
     </section> 
    </section> 
    <section> 
     <title>Section 1.2</title> 
     <para>...</para> 
     <footnote>...</footnote> 
    </section> 
</section> 
<section> 
    <title>Section 2</title> 
    <para>...</para> 
    <list_1>...</list_1> 
    <section> 
     <title>Section 2.1</title> 
     <para>...</para> 
     <list_1>...</list_1> 
     <list_2>...</list_2> 
     <section> 
      <title>Section 2.1.1</title> 
      <para>...</para> 
     </section> 
    </section> 
    <section> 
     <title>Section 2.2</title> 
     <para>...</para> 
     <footnote>...</footnote> 
    </section> 
</section> 

我也試過一段時間才能找到從@JLRishe原來的解決方案這基礎的解決方案。所以我找到了一個並且喜歡把它作爲一個可能的答案放在這裏。我希望能有一個更易理解的解決方案。

回答

1

這裏我目前的解決方案

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:exsl="http://exslt.org/common" 
    extension-element-prefixes="exsl"> 

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

    <xsl:variable name="headerLevel_txt"> 
     <heading name="__none__" level="0"/> 
     <heading name="heading_1" level="1"/> 
     <heading name="heading_2" level="2"/> 
     <heading name="heading_3" level="3"/> 
    </xsl:variable> 

    <xsl:variable name="headerLevel" select="exsl:node-set($headerLevel_txt)" /> 


    <xsl:template match="@* | node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()" > 
       <xsl:with-param name="ch" select="h1" /> 
      </xsl:apply-templates> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="/*"> 
     <xsl:copy> 
      <xsl:apply-templates select="heading_1" /> 
     </xsl:copy> 
    </xsl:template> 


    <xsl:template match="heading_1 | heading_2 | heading_3" > 
     <xsl:param name="previousheaader" select="'__none__'" /> 

     <xsl:variable name="endOfLevel" > 
      <xsl:call-template name="endOfLevel"> 
       <xsl:with-param name="currentheaader" select="name()" /> 
       <xsl:with-param name="previousheaader" select="$previousheaader" /> 
      </xsl:call-template> 
     </xsl:variable> 

     <xsl:if test ="$endOfLevel != 'end'" > 
      <section> 
       <title> 
        <xsl:value-of select="normalize-space(.)"/> 
       </title> 

       <!-- following siblings including next heading --> 
       <xsl:variable name="fsinh" select="following-sibling::*[ 
           generate-id(preceding-sibling::* 
              [ 
              name() = 'heading_1' or name() = 'heading_2' or name() = 'heading_3' 
              ][1] 
             ) = generate-id(current()) ]" /> 

        <xsl:apply-templates select="$fsinh[ position()!=last()]" > 
         <xsl:with-param name="previousheaader" select="name()" /> 
        </xsl:apply-templates> 

       <!-- following siblings heading same as next --> 
       <xsl:variable name="fshsan" select="following-sibling::*[ 
           name() = name($fsinh[last()]) and 
           generate-id(preceding-sibling::* 
              [ 
              name() = name(current()) 
              ][1] 
             ) = generate-id(current()) ]" /> 
       <xsl:apply-templates select="$fshsan" > 
        <xsl:with-param name="previousheaader" select="name()" /> 
       </xsl:apply-templates> 

      </section> 
     </xsl:if> 

    </xsl:template> 

    <xsl:template name="endOfLevel"> 
     <xsl:param name ="currentheaader"/> 
     <xsl:param name ="previousheaader"/> 
     <!-- The previous heading ends if the current has an higher level (smaller level number)--> 
     <xsl:variable name ="cl" select="number($headerLevel/heading[@name=$currentheaader]/@level)"/> 
     <xsl:variable name ="pl" select="number($headerLevel/heading[@name=$previousheaader]/@level)"/> 
     <xsl:if test ="$cl &lt; $pl">end</xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

它會生成以下的輸出:

<root> 
    <section> 
    <title>Section 1</title> 
    <para>...</para> 
    <list_1>...</list_1> 
    <section> 
     <title>Section 1.1</title> 
     <para>...</para> 
     <section> 
     <title>Section 1.1.1</title> 
     <para>...</para> 
     <list_1>...</list_1> 
     </section> 
    </section> 
    <section> 
     <title>Section 1.2</title> 
     <para>...</para> 
     <footnote>...</footnote> 
    </section> 
    </section> 
    <section> 
    <title>Section 2</title> 
    <para>...</para> 
    <list_1>...</list_1> 
    <section> 
     <title>Section 2.1</title> 
     <para>...</para> 
     <list_1>...</list_1> 
     <list_2>...</list_2> 
     <section> 
     <title>Seciton 2.1.1</title> 
     <para>...</para> 
     </section> 
    </section> 
    <section> 
     <title>Section 2.2</title> 
     <para>...</para> 
     <footnote>...</footnote> 
    </section> 
    </section> 
</root> 
3

這裏是一個通用的解決方案,適用於任何標題深入的工作:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> 
    <xsl:key name="kChildHeader" 
      match="/*/*[starts-with(local-name(), 'heading_')]" 
      use="generate-id(preceding-sibling::* 
         [local-name() = 
         concat('heading_', 
           substring-after(local-name(current()), '_') - 1 
          )][1] 
          )"/> 
    <xsl:key name="kChildItem" 
      match="/*/*[not(starts-with(local-name(), 'heading_'))]" 
      use="generate-id(preceding-sibling::* 
          [starts-with(local-name(), 'heading_')][1])"/> 

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

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:apply-templates select="heading_1" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[starts-with(local-name(), 'heading_')]"> 
    <xsl:copy> 
     <xsl:apply-templates select="key('kChildHeader', generate-id()) | 
            key('kChildItem', generate-id())"/> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

在您的示例輸入上運行時,會生成:

<root> 
    <heading_1> 
    <para>...</para> 
    <list_1>...</list_1> 
    <heading_2> 
     <para>...</para> 
     <heading_3> 
     <para>...</para> 
     <list_1>...</list_1> 
     </heading_3> 
    </heading_2> 
    <heading_2> 
     <para>...</para> 
     <footnote>...</footnote> 
    </heading_2> 
    </heading_1> 
    <heading_1> 
    <para>...</para> 
    <list_1>...</list_1> 
    <heading_2> 
     <para>...</para> 
     <list_1>...</list_1> 
     <list_2>...</list_2> 
     <heading_3> 
     <para>...</para> 
     </heading_3> 
    </heading_2> 
    <heading_2> 
     <para>...</para> 
     <footnote>...</footnote> 
    </heading_2> 
    </heading_1> 
</root> 
+0

我不得不意識到,這個很好的解決方案並不適用於所有的xlst處理器(例如, xsltproc),因爲xls:key中的current()存在問題 – 2013-05-08 15:58:02