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