Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

XSLT : Turning Siblings into Children

Status
Not open for further replies.

wolfie78uk

Programmer
Jul 3, 2002
35
GB
Hi,

Imagine that I have the following XML document:

<article>
<heading level="1">Heading1</heading>
<heading level="2">Sub1</heading>
<paragraph>1st <bold>block</bold> of info</paragraph>
<heading level="1">Heading2</heading>
<heading level="2">Sub2</heading>
<paragraph>2nd <italic>block</italic> of info</paragraph>
<heading level="1">Heading3</heading>
<heading level="2">Sub3</heading>
<paragraph>3rd <strong>block</strong> of info</paragraph>
</article>

and I want to transform it into :


<article>
<section>
<heading>Heading1</heading>
<sub-heading>Sub1</sub-heading>
<paragraph>1st <bold>block</bold> of info</paragraph>
</section>
<section>
<heading>Heading2</heading>
<sub-heading>Sub2</sub-heading>
<paragraph>2nd <italic>block</italic> of info</paragraph>
</section>
<section>
<heading>Heading3</heading>
<sub-heading>Sub3</sub-heading>
<paragraph>3rd <strong>block</strong> of info</paragraph>
</section>
</article>

I am using the following XSL :

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:template match="/article">
<article>
<xsl:apply-templates select="heading" />
</article>
</xsl:template>

<xsl:template match="heading">
<xsl:choose select=".">
<xsl:when test ="@level &lt; 2">
<xsl:element name="section">
<xsl:element name="heading">
<xsl:value-of select="." />
</xsl:element>
<xsl:apply-templates select="following-sibling::*" />
</xsl:element>
</xsl:when>
<xsl:eek:therwise>
<xsl:element name="sub-heading">
<xsl:value-of select="." />
</xsl:element>
</xsl:eek:therwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

Unfortunately, it is not producing the desired result because I need it to stop at each "heading".

Does anyone have any ideas about how I can achieve my goal (without resorting to writing parsing code).

Thanks,

Simon
 
You may do it like this modulo whitespace placement, but it is a norm in xslt that ways are manifold.
[tt]
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:eek:utput method="xml" />

<xsl:template match="/">
<xsl:element name="article">
<xsl:apply-templates />
</xsl:element>
</xsl:template>

<xsl:template match="heading[@level='1']">
<xsl:element name="section">
<xsl:element name="heading">
<xsl:value-of select="." />
</xsl:element>
<xsl:element name="subheading">
<xsl:value-of select="following-sibling::heading" />
</xsl:element>
<xsl:element name="paragraph">
<xsl:value-of select="following-sibling::paragraph/child::node()[position()=1]" />
<xsl:variable name="x" select="local-name(following-sibling::paragraph/child::node()[position()=2])" />
<xsl:element name="{$x}">
<xsl:value-of select="following-sibling::paragraph/child::node()[position()=2]" />
</xsl:element>
<xsl:value-of select="following-sibling::paragraph/child::node()[position()=3]" />
</xsl:element>
</xsl:element>
</xsl:template>

<xsl:template match="//text()">
</xsl:template>

</xsl:stylesheet>
[/tt]
 
Thanks for your help. After much trial and error, I have the following "solution" (I have never used modes and ,although the positioning solution works, I need something more flexible as in the final solution there may be many elements between headings):

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:key name="paras" match="paragraph | link | heading[@level=2]"
use="preceding-sibling::heading[1]/text()" />

<xsl:template match="article">
<article>
<xsl:apply-templates select="heading[@level=1]" />
</article>
</xsl:template>

<xsl:template match="heading[@level=1]">
<xsl:element name="section">
<xsl:copy-of select="." />
<xsl:copy-of select="key('paras', text())" />
</xsl:element>
</xsl:template>

</xsl:stylesheet>


However, I have a problem with "level 2 headers". Using the following XML with all of the level 2 headers commented out, all is well. However, as soon as the first level 2 header ("sub 1") is uncommented, the first section processes nothing past the first level 2 header and ONLY the paragraph element is processed in the other sections. When I replaced the level 2 headers with the element "subheading" that was fine (although I cannot do this in the final solution).

The text.xml file is :

<article>
<heading level="1">Heading1</heading>
<!--heading level="2">Sub1</heading-->
<paragraph>1st <bold>block</bold> of info</paragraph>
<paragraph>Sneaky</paragraph>
<link>Points somewhere</link>
<heading level="1">Heading2</heading>
<!--heading level="2">Sub2</heading-->
<paragraph>2nd <italic>block</italic> of info</paragraph>
<heading level="1">Heading3</heading>
<!--heading level="2">Sub3</heading-->
<paragraph>3rd <strong>block</strong> of info</paragraph>
</article>

Any help would be appreciated.

Thanks,

Simon
 
Ooooops. Found the error (must remember to check code more thoroughly before posting replies).
The offending line has been altered from:

use="preceding-sibling::heading[1]/text()" />

to:

use="preceding-sibling::heading[@level=1][1]/text()"

See below:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:key name="paras" match="paragraph | link | heading[@level=2]"
use="preceding-sibling::heading[@level=1][1]/text()" />

<xsl:template match="article">
<article>
<xsl:apply-templates select="heading[@level=1]" />
</article>
</xsl:template>

<xsl:template match="heading[@level=1]">
<xsl:element name="section">
<xsl:copy-of select="." />
<xsl:copy-of select="key('paras', text())" />
</xsl:element>
</xsl:template>

</xsl:stylesheet>

Thanks for your help.
 
Read with interest op's solution. Just a note. It seems therefore the original requirements on <heading> and <subheading> in the transformed is abolished. Nice use of key and copy-of. The post-backs are appreciated.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top