创建一个分层的xml表单,一个“平面”的xml,就像书的描述一样

时间:2013-04-24 09:28:35

标签: xslt

这是向"Select all of an element between the current element and the next of the current element"提问的后续问题。即使我不确定创建一个新问题是否是正确的方法我仍然这样做。因为原来的问题得到了回答,但事后发生了变化。所以在我看来,改变的问题是开放的。此外,我认为改变的问题应该放回到统计数据中,如果它符合答案。

问题是如何创建一个层次结构的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的原始解决方案找到解决方案。所以我找到了一个并且喜欢把它作为答案作为一种可能性。我希望有一个更易于理解的解决方案。

2 个答案:

答案 0 :(得分: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>

答案 1 :(得分: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>