2013-04-24 52 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