Trouble with XSL

C

carra

Hi
If I have some XML that looks like this...

<heading1>fdfdfdfdf</heading>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
<heading2>gfhgjg</heading>
<para>text4</para>

How do I transform it with XSL so that all the para's and tables under
the headings are wrapped within the heading so that it looks like
this....

<heading1>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
</heading>
<heading2>
<para>text4</para>
</heading2>

Any help would be appreciated

Regards
Andy
 
J

Johannes Koch

Hi
If I have some XML that looks like this...

<heading1>fdfdfdfdf</heading>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
<heading2>gfhgjg</heading>
<para>text4</para>

How do I transform it with XSL so that all the para's and tables under
the headings are wrapped within the heading so that it looks like
this....

<heading1>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
</heading>
<heading2>
<para>text4</para>
</heading2>

Are you sure you want to throw away the heading text and make the other
elements children of heading elements? Sounds illogical to me. Otherwise
see the thread "HTML Parsing Question" from 31 December 2006.
 
G

George Bina

Hi Andy,

This is known as positional grouping.
In XSLT 2.0 you can have for instance something like

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:eek:utput indent="yes"/>
<xsl:template match="/test">
<xsl:for-each-group select="*"
group-starting-with="heading1|heading2">
<xsl:copy>
<xsl:for-each select="current-group()[position()>1]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Applied on
<test>
<heading1>fdfdfdfdf</heading1>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
<heading2>gfhgjg</heading2>
<para>text4</para>
</test>
will give you
<?xml version="1.0" encoding="UTF-8"?>
<heading1>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
</heading1>
<heading2>
<para>text4</para>
</heading2>

An XSLT 1.0 solution is to walk along following sibling axes until the
next node, like below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:eek:utput indent="yes"/>
<xsl:template match="/test">
<xsl:for-each select="heading1|heading2">
<xsl:variable name="headerId" select="generate-id(.)"/>
<xsl:copy>
<xsl:for-each select="following-sibling::*[not(self::heading2
or self::heading1)
and generate-id(preceding-sibling::*[self::heading1 or
self::heading2][1])=$headerId]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Best Regards,
George
 
P

p.lepin

If I have some XML that looks like this...

No you don't. That's not XML. Where's the root element?
<heading1>fdfdfdfdf</heading>

For that matter, this is not well-formed.
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
<heading2>gfhgjg</heading>

Neither is this.
<para>text4</para>

So you can't be bothered to spend fifteen minutes reading
XSLT/XPath tutorial, you can't be bothered to spend fifteen
*seconds* proof-reading your post, and you're asking for
help. Ok then.
How do I transform it with XSL so that all the para's and
tables under the headings are wrapped within the heading
so that it looks like this....

<heading1>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
</heading>
<heading2>
<para>text4</para>
</heading2>

Since you failed to mention what should happen to headings'
children and attributes, I won't address that.

Here's a little something for you to chew on. Have fun:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key
name="heading"
match="*[not(starts-with(local-name(),'heading'))]"
use=
"
generate-id
(
preceding-sibling::*
[starts-with(local-name(),'heading')][1]
)
"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="*[starts-with(local-name(),'heading')]">
<xsl:copy>
<xsl:apply-templates
select="key('heading',generate-id())" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template
match=
"
*
[not(starts-with(local-name(),'heading'))]
[
preceding-sibling::*
[starts-with(local-name(),'heading')]
]
"/>
<xsl:template match="@*|node()" mode="copy">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="copy"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
 
C

carra

If I have some XML that looks like this...

No you don't. That's not XML. Where's the root element?
<heading1>fdfdfdfdf</heading>

For that matter, this is not well-formed.
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
<heading2>gfhgjg</heading>

Neither is this.
<para>text4</para>

So you can't be bothered to spend fifteen minutes reading
XSLT/XPath tutorial, you can't be bothered to spend fifteen
*seconds* proof-reading your post, and you're asking for
help. Ok then.
How do I transform it with XSL so that all the para's and
tables under the headings are wrapped within the heading
so that it looks like this....

<heading1>
<para>text1</para>
<para>text2</para>
<table>Table structure</table>
<para>text3</para>
</heading>
<heading2>
<para>text4</para>
</heading2>

Since you failed to mention what should happen to headings'
children and attributes, I won't address that.

Here's a little something for you to chew on. Have fun:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key
name="heading"
match="*[not(starts-with(local-name(),'heading'))]"
use=
"
generate-id
(
preceding-sibling::*
[starts-with(local-name(),'heading')][1]
)
"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="*[starts-with(local-name(),'heading')]">
<xsl:copy>
<xsl:apply-templates
select="key('heading',generate-id())" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template
match=
"
*
[not(starts-with(local-name(),'heading'))]
[
preceding-sibling::*
[starts-with(local-name(),'heading')]
]
"/>
<xsl:template match="@*|node()" mode="copy">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="copy"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

That's a big help people, thanks a lot.

BTW I forgot to mention the XML I used was an example fragment, not
real code, so forgive typo's.
No need to get abusive though, eh Pavel.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,007
Messages
2,570,266
Members
46,865
Latest member
AveryHamme

Latest Threads

Top