I
I.M. Postor
Hello,
Maybe this is a case of positional grouping, but my level of xslt has me
stuck here.
I have some XML where some elements haven't the proper name (level2
instead of level3) and aren't properly embedded. A level2 element should
always be embedded in a level1 element. a level3 element should always
be embedded in a level2 element. An element <levelX type="single">
cannot embed other levelX elements. The amount of elements <levelX
type="single"> _to_be_embedded_ depends on the value of the attribute
"embed" above.
Input:
<?xml version="1.0" ?>
<root>
<level1 type="other"><name>A</name>
<level2 type="group" embed="4"><name>B</name>
<level3 type="single"><name>C</name>
</level3>
</level2> <!-- level2 type="group" is closed here; too soon-->
<level2 type="single"><name>D</name>
</level2>
<level2 type="single"><name>E</name>
</level2>
<level2 type="single"><name>F</name>
</level2>
<!-- level2 type="group" closing tag should be here -->
<level2 type="single"><name>G</name>
</level2>
<level2 type="single"><name>H</name>
</level2>
<level2 type="single"><name>I</name>
</level2>
<!-- ...-->
</level1>
</root>
So in this example the element with name C is already properly embedded,
but not D, E, F.
I have this stylesheet:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xslutput method="xml" indent="yes" />
<xsl:template match="*/comment()" />
<!-- identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/level1/level2[@type='group']">
<xsl:variable name="volumes" select="@embed" as="xs:integer" />
<xsl:variable name="already_embedded"
select="count(child::level3)" as="xs:integer" />
<xsl:variable name="to_embed"
select="$volumes - $already_embedded" as="xs:integer" />
<xsl:element name="level2">
<xsl:copy-of select="@*" />
<xsl:copy-of select="node()" />
<xsl:for-each
select="following-sibling::level2[position() <= $to_embed]">
<xsl:element name="level3X"> <!-- X just as a marker -->
<xsl:copy-of select="@*" />
<xsl:copy-of select="node()" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
which produces:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<level1 type="other">
<name>A</name>
<level2 type="group" embed="4">
<name>B</name>
<level3 type="single">
<name>C</name>
</level3>
<level3X type="single">
<name>D</name>
</level3X>
<level3X type="single">
<name>E</name>
</level3X>
<level3X type="single">
<name>F</name>
</level3X>
</level2>
<level2 type="single">
<name>D</name>
</level2>
<level2 type="single">
<name>E</name>
</level2>
<level2 type="single">
<name>F</name>
</level2>
<level2 type="single">
<name>G</name>
</level2>
<level2 type="single">
<name>H</name>
</level2>
<level2 type="single">
<name>I</name>
</level2>
</level1>
</root>
where of course D-F are doubled, which is not what I want.
I think I might have to do something clever with keys and generate-id() like:
<xsl:template match="/root/level1/level2[@type='single']">
<xsl:if generate-id(.) != generate-id(key('???', ???))
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
But, given that this is the right approach, I have no idea how to declare a key
in the toplevel (directly under xsl:stylesheet), give it a value under the
for-each loop _and_ get to that value in the level2[@type='single'] template.
Any help is appreciated,
Cheers
Maybe this is a case of positional grouping, but my level of xslt has me
stuck here.
I have some XML where some elements haven't the proper name (level2
instead of level3) and aren't properly embedded. A level2 element should
always be embedded in a level1 element. a level3 element should always
be embedded in a level2 element. An element <levelX type="single">
cannot embed other levelX elements. The amount of elements <levelX
type="single"> _to_be_embedded_ depends on the value of the attribute
"embed" above.
Input:
<?xml version="1.0" ?>
<root>
<level1 type="other"><name>A</name>
<level2 type="group" embed="4"><name>B</name>
<level3 type="single"><name>C</name>
</level3>
</level2> <!-- level2 type="group" is closed here; too soon-->
<level2 type="single"><name>D</name>
</level2>
<level2 type="single"><name>E</name>
</level2>
<level2 type="single"><name>F</name>
</level2>
<!-- level2 type="group" closing tag should be here -->
<level2 type="single"><name>G</name>
</level2>
<level2 type="single"><name>H</name>
</level2>
<level2 type="single"><name>I</name>
</level2>
<!-- ...-->
</level1>
</root>
So in this example the element with name C is already properly embedded,
but not D, E, F.
I have this stylesheet:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xslutput method="xml" indent="yes" />
<xsl:template match="*/comment()" />
<!-- identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/level1/level2[@type='group']">
<xsl:variable name="volumes" select="@embed" as="xs:integer" />
<xsl:variable name="already_embedded"
select="count(child::level3)" as="xs:integer" />
<xsl:variable name="to_embed"
select="$volumes - $already_embedded" as="xs:integer" />
<xsl:element name="level2">
<xsl:copy-of select="@*" />
<xsl:copy-of select="node()" />
<xsl:for-each
select="following-sibling::level2[position() <= $to_embed]">
<xsl:element name="level3X"> <!-- X just as a marker -->
<xsl:copy-of select="@*" />
<xsl:copy-of select="node()" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
which produces:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<level1 type="other">
<name>A</name>
<level2 type="group" embed="4">
<name>B</name>
<level3 type="single">
<name>C</name>
</level3>
<level3X type="single">
<name>D</name>
</level3X>
<level3X type="single">
<name>E</name>
</level3X>
<level3X type="single">
<name>F</name>
</level3X>
</level2>
<level2 type="single">
<name>D</name>
</level2>
<level2 type="single">
<name>E</name>
</level2>
<level2 type="single">
<name>F</name>
</level2>
<level2 type="single">
<name>G</name>
</level2>
<level2 type="single">
<name>H</name>
</level2>
<level2 type="single">
<name>I</name>
</level2>
</level1>
</root>
where of course D-F are doubled, which is not what I want.
I think I might have to do something clever with keys and generate-id() like:
<xsl:template match="/root/level1/level2[@type='single']">
<xsl:if generate-id(.) != generate-id(key('???', ???))
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
But, given that this is the right approach, I have no idea how to declare a key
in the toplevel (directly under xsl:stylesheet), give it a value under the
for-each loop _and_ get to that value in the level2[@type='single'] template.
Any help is appreciated,
Cheers