Create an hierarchical xml form an “flat” xml a like book description

后端 未结 2 1897
眼角桃花
眼角桃花 2021-01-17 05:36

This is a following up question to question to \"Select all of an element between the current element and the next of the current element\". Even If I\'m not sure if creatin

相关标签:
2条回答
  • 2021-01-17 06:05

    Here is a generic solution that will work for any heading depth:

    <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>
    

    When run on your sample input, this produces:

    <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 讨论(0)
  • 2021-01-17 06:09

    Here my current solution

    <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>
    

    It will generate the following output:

    <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>
    
    0 讨论(0)
提交回复
热议问题