How to convert flat xml data to hierarchical data xml 2

我的梦境 提交于 2020-08-10 19:14:27

问题


Following my question XSL change structure of ODT XML file, I cannot work out the solution given in this thread How to convert flat xml data to hierarchical data xml in my case.

I slightly changed the input xml (h instead of p for titles) :

<body>      
    <h class="head1">1: Heading level 1</h>
    <p>some text here</p>
    <p>some text here</p>
    <h class="head2">1.1: Heading  level 2</h>
    <p>some text here</p>
    <p>some text here</p>        
    <h class="head3">1.1.1: Heading  level 3</h>
    <p>some text here</p>
    <p>some text here</p>        
    <h class="head1">2: Heading level 1</h>
    <h class="head2">2.1: Heading  level 2</h>
    <p>some text here</p>
    <p>some text here</p>        
    <h class="head3">2.1.1: Heading  level 3</h>
    <p>some text here</p>
    <p>some text here</p>        
    <h class="head3">2.1.2: Heading  level 3</h>
    <p>some text here</p>
    <p>some text here</p>        
</body>

And i don't know how to adapt the following Xsl

<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="node()|@*">
    <xsl:copy>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

<xsl:function name="mf:group" as="element(section)*">
    <xsl:param name="entries" as="element(p)*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$entries"
        group-starting-with="p[@class = concat('head',$level)]">
        <xsl:variable name="P_ID" select="generate-id(.)"/>
        <section name="{@class}">
            <title>
                <xsl:value-of select="."/>
            </title>
            <xsl:if test="following-sibling::p[1][not(@class)]">
                <ps>
                    <xsl:apply-templates
                        select="following-sibling::p[not(@class)][generate-id(preceding-sibling::p[@class][1]) = $P_ID]"
                    />
                </ps>
            </xsl:if>

            <xsl:sequence select="mf:group(current-group() except ., ($level + 1))"/>
        </section>
    </xsl:for-each-group>
</xsl:function>

<xsl:template match="body">
    <xsl:copy>
        <xsl:sequence select="mf:group(p[contains(@class,'head')], 1)"/>
    </xsl:copy>
</xsl:template>

To get this result

<body>
    <section name="head1">
        <title>1: Heading level 1</title>
        <ps>
            <p>some text here</p>
            <p>some text here</p>
        </ps>
        <section name="head2">
            <title>1.1: Heading  level 2</title>
            <ps>
                <p>some text here</p>
                <p>some text here</p>
            </ps>
            <section name="head3">
                <title>1.1.1: Heading  level 3</title>
                <ps>
                    <p>some text here</p>
                    <p>some text here</p>
                </ps>
            </section>
        </section>
    </section>
    <section name="head1">
        <title>2: Heading level 1</title>
        <section name="head2">
            <title>2.1: Heading  level 2</title>
            <ps>
                <p>some text here</p>
                <p>some text here</p>
            </ps>
            <section name="head3">
                <title>2.1.1: Heading  level 3</title>
                <ps>
                    <p>some text here</p>
                    <p>some text here</p>
                </ps>
            </section>
            <section name="head3">
                <title>2.1.2: Heading  level 3</title>
                <ps>
                    <p>some text here</p>
                    <p>some text here</p>
                </ps>
            </section>
        </section>
    </section>
</body>

回答1:


An adaption and simplification of my answer in the referenced previous question would be

<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="#all"
  version="3.0">

<xsl:output indent="yes"/>

<xsl:mode on-no-match="shallow-copy"/>

<xsl:function name="mf:group" as="element()*">
    <xsl:param name="elements" as="element()*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$elements" group-starting-with="h[@class = concat('head', $level)]">
       <xsl:choose>
              <xsl:when test="not(self::h[@class = concat('head', $level)])">
                <xsl:where-populated>
                    <ps>
                      <xsl:apply-templates select="current-group()"/>
                    </ps>
                </xsl:where-populated>
              </xsl:when>
              <xsl:otherwise>
                <section name="{@class}">
                  <title>
                      <xsl:apply-templates select="node()"/>
                  </title>
                  <xsl:sequence select="mf:group(current-group() except ., ($level + 1))"/>
                </section>
              </xsl:otherwise>
       </xsl:choose>
    </xsl:for-each-group>
</xsl:function>

<xsl:template match="body">
  <xsl:copy>
    <xsl:sequence select="mf:group(*, 1)"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jxDjimV

Uses XSLT 3 xsl:mode to declare the identity transformation as the base transformation but you could spell that out as a template for an XSLT 2 processor. And the xsl:where-populated is also XSLT 3 but could be replaced with a more specific test in the xsl:when test" if needed.



来源:https://stackoverflow.com/questions/63173809/how-to-convert-flat-xml-data-to-hierarchical-data-xml-2

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!