问题
I have to convert a flat xml to hierarchical xml. I have no idea for this task. Below is input for conversion.
Input:-
<body>
<p class="title">Article Title</p>
<p class="Authors">abc, pqr and xyz</p>
<p class="intro">here is introdution text......</p>
<p class="head1">1: Heading level 1</p>
<p>some text here</p>
<p>some text here</p>
<p class="head2">1.1: Heading level 2</p>
<p>some text here</p>
<p>some text here</p>
<p class="head3">1.1.1: Heading level 3</p>
<p>some text here</p>
<p>some text here</p>
<p class="head1">2: Heading level 1</p>
<p class="head2">2.1: Heading level 2</p>
<p>some text here</p>
<p>some text here</p>
<p class="head3">2.1.1: Heading level 3</p>
<p>some text here</p>
<p>some text here</p>
<p class="head3">2.1.2: Heading level 3</p>
<p>some text here</p>
<p>some text here</p>
</body>
I want to convert it to below given xml.
Output:-
<article>
<title>Article Title</title>
<Authors>abc, pqr and xyz</Authors>
<introduction>here is introdution text......</introduction>
<body>
<sec level="1">
<title>1: Heading level 1</title>
<p>some text here</p>
<p>some text here</p>
<sec level="2">
<title>1.1: Heading level 2</title>
<p>some text here</p>
<p>some text here</p>
<sec level="3">
<title>1.1.1: Heading level 3</title>
<p>some text here</p>
<p>some text here</p>
</sec>
</sec>
</sec>
<sec level="1">
<title>2: Heading level 1</title>
<sec level="2">
<title>2.1: Heading level 2</title>
<p>some text here</p>
<p>some text here</p>
<sec level="3">
<title>2.1.1: Heading level 3</title>
<p>some text here</p>
<p>some text here</p>
</sec>
<sec level="3">
<title>2.1.2: Heading level 3</title>
<p>some text here</p>
<p>some text here</p>
</sec>
</sec>
</sec>
</body>
</article>
I havn't any idea to convert it using xslt.
I am getting below Output:-
<article>
<p class="title">Article Title</p>
<p class="Authors">abc, pqr and xyz</p>
<p class="intro">here is introdution text......</p>
<body>
<p class="head1">1: Heading level 1</p>
<p>some text here</p>
<p>some text here</p>
<p class="head2">1.1: Heading level 2</p>
<p>some text here</p>
<p>some text here</p>
<p class="head3">1.1.1: Heading level 3</p>
<p>some text here</p>
<p>some text here</p>
<p class="head1">2: Heading level 1</p>
<p class="head2">2.1: Heading level 2</p>
<p>some text here</p>
<p>some text here</p>
<p class="head3">2.1.1: Heading level 3</p>
<p>some text here</p>
<p>some text here</p>
<p class="head3">2.1.2: Heading level 3</p>
<p>some text here</p>
<p>some text here</p>
</body>
</article>
My code is :-
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="html">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="head"/>
<xsl:template match="body">
<xsl:element name="article">
<xsl:apply-templates select="p[@class='title']|p[@class='Authors']|p[@class='intro']"/>
<xsl:element name="body">
<xsl:apply-templates select="p[preceding-sibling::p[@class='intro']]"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
回答1:
You can use the below xslt code for expected output:
<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>
回答2:
Here is an example that uses a recursive function to achieve the nesting and of course some further templates to transform the various p
elements:
<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 select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:function name="mf:group" as="element()*">
<xsl:param name="elements" as="element(p)*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$elements" group-starting-with="p[@class = concat('head', $level)]">
<xsl:choose>
<xsl:when test="not(self::p[@class = concat('head', $level)])">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<sec level="{$level}">
<xsl:apply-templates select="."/>
<xsl:sequence select="mf:group(current-group() except ., ($level + 1))"/>
</sec>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="body">
<article>
<xsl:apply-templates select="p[@class = 'head1'][1]/preceding-sibling::*"/>
<xsl:copy>
<xsl:sequence select="mf:group(p[@class = 'head1'][1]/(., following-sibling::*), 1)"/>
</xsl:copy>
</article>
</xsl:template>
<xsl:template match="p[@class = 'title'] | p[starts-with(@class, 'head')]">
<title>
<xsl:apply-templates/>
</title>
</xsl:template>
<xsl:template match="p[@class = 'intro']">
<introduction>
<xsl:apply-templates/>
</introduction>
</xsl:template>
<xsl:template match="p[@class = 'Authors']">
<Authors>
<xsl:apply-templates/>
</Authors>
</xsl:template>
</xsl:stylesheet>
回答3:
In XSLT 1.0:-
<?xml version="1.0" encoding="UTF-8"?>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="html">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="head"/>
<xsl:key name="Body" match="p[not(@class)]"
use="generate-id((preceding-sibling::p[@class='Head1'] | preceding-sibling::p[@class='Head2']| preceding-sibling::p[@class='Head3']| preceding-sibling::p[@class='Head4']| preceding-sibling::p[@class='Head5']| preceding-sibling::p[@class='Head6'])[last()])"/>
<xsl:key name="h2" match="p[@class='Head2']"
use="generate-id(preceding-sibling::p[@class='Head1'][1])"/>
<xsl:key name="h3" match="p[@class='Head3']"
use="generate-id(preceding-sibling::p[@class='Head2'][1])"/>
<xsl:key name="h4" match="p[@class='Head4']"
use="generate-id(preceding-sibling::p[@class='Head3'][1])"/>
<xsl:key name="h5" match="p[@class='Head5']"
use="generate-id(preceding-sibling::p[@class='Head4'][1])"/>
<xsl:key name="h6" match="p[@class='Head6']"
use="generate-id(preceding-sibling::p[@class='Head5'][1])"/>
<xsl:template match="body">
<xsl:element name="article">
<xsl:apply-templates
select="p[@class='title']|p[@class='Title']|p[@class='Authors']|p[@class='intro']"/>
<xsl:element name="body">
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
<xsl:apply-templates select="descendant::p[@class='Head1']" mode="h1"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="p" mode="h1">
<sec level="1">
<title>
<xsl:apply-templates/>
</title>
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
<xsl:apply-templates select="key('h2',generate-id())" mode="h2"/>
</sec>
</xsl:template>
<xsl:template match="p" mode="h2">
<sec level="2">
<title>
<xsl:apply-templates/>
</title>
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
<xsl:apply-templates select="key('h3',generate-id())" mode="h3"/>
</sec>
</xsl:template>
<xsl:template match="p" mode="h3">
<sec level="3">
<title>
<xsl:apply-templates/>
</title>
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
<xsl:apply-templates select="key('h4',generate-id())" mode="h4"/>
</sec>
</xsl:template>
<xsl:template match="p" mode="h4">
<sec level="4">
<title>
<xsl:apply-templates/>
</title>
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
<xsl:apply-templates select="key('h5',generate-id())" mode="h5"/>
</sec>
</xsl:template>
<xsl:template match="p" mode="h5">
<sec level="5">
<title>
<xsl:apply-templates/>
</title>
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
<xsl:apply-templates select="key('h6',generate-id())" mode="h6"/>
</sec>
</xsl:template>
<xsl:template match="p" mode="h6">
<sec level="6">
<title>
<xsl:apply-templates/>
</title>
<xsl:apply-templates select="key('Body',generate-id())" mode="Body"/>
</sec>
</xsl:template>
<xsl:template match="p" mode="Body">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
来源:https://stackoverflow.com/questions/25499786/how-to-convert-flat-xml-data-to-hierarchical-data-xml