469,601 Members | 2,105 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,601 developers. It's quick & easy.

XSLT - Sub-grouping in mixed nodes

MRe
Hi,

Is it possible using XSLT to transform this..

<test>
<b>0</b>
<a>1</a>
<a>2</a>
<b>3</b>
<b>4</b>
5
<b>6</b>
<b>7</b>
<b>8</b>
</test>

..into this (it's basically just wrapping groups of <b>s in a <c>)..

<test>
<c>
<b>0</b>
</c>
<a>1</a>
<a>2</a>
<c>
<b>3</b>
<b>4</b>
</c>
5
<c>
<b>6</b>
<b>7</b>
<b>8</b>
</c>
</test>

..that is, treat, in a mix of nodes, a specified group of nodes
(nodes that appear one-after-the-other, having no node outside that
group (except ignored empty text() nodes) between them) as a block?
(in the example above, <bis the only chosen node in this group)

The closest I've come to getting this is to call a recursive
template, passing child::*[1] initially, and following-sibling::*[1]
for each recursive step, and also passing a 'block' parameter that, if
false and <bis encountered, puts a <cin and sets block to true,
and if true and <ais encountered, puts a </cin and sets to false.
However, this won't work as it won't put a </cin if the last element
is a <b>.

All other attempts I've made at a solution I'd prefer to keep to
myself [embarrassed]

Thank you,
Kind regards,
Eliott
Aug 14 '08 #1
4 1762
MRe wrote:
Is it possible using XSLT to transform this..

<test>
<b>0</b>
<a>1</a>
<a>2</a>
<b>3</b>
<b>4</b>
5
<b>6</b>
<b>7</b>
<b>8</b>
</test>

..into this (it's basically just wrapping groups of <b>s in a <c>)..

<test>
<c>
<b>0</b>
</c>
<a>1</a>
<a>2</a>
<c>
<b>3</b>
<b>4</b>
</c>
5
<c>
<b>6</b>
<b>7</b>
<b>8</b>
</c>
</test>
With XSLT 2.0 (as supported by Saxon http://saxon.sourceforge.net/,
Gestalt http://gestalt.sourceforge.net/, and Altova
http://www.altova.com/altovaxml.html) you can solve that as follows:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">

<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/test">
<xsl:copy>
<xsl:for-each-group select="node()"
group-adjacent="boolean(self::b)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<c>
<xsl:copy-of select="current-group()"/>
</c>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>
Or do you need an XSLT 1.0 solution?
--

Martin Honnen
http://JavaScript.FAQTs.com/
Aug 14 '08 #2
MRe wrote:
The closest I've come to getting this is to call a recursive
template, passing child::*[1] initially, and following-sibling::*[1]
for each recursive step, and also passing a 'block' parameter that, if
false and <bis encountered, puts a <cin and sets block to true,
and if true and <ais encountered, puts a </cin and sets to false.
However, this won't work as it won't put a </cin if the last element
is a <b>.
With XSLT 1.0 you can solve such problems by processing sibling by
sibling, here is a sample stylesheet:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/test">
<xsl:copy>
<xsl:apply-templates select="node()[1]" mode="group"/>
</xsl:copy>
</xsl:template>

<xsl:template match="test/b" mode="group">
<c>
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1][self::b]"/>
</c>
<xsl:apply-templates
select="following-sibling::node()[not(self::b)][1]" mode="group"/>
</xsl:template>

<xsl:template match="test/b">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1][self::b]"/>
</xsl:template>

<xsl:template match="test/node()[not(self::b)]" mode="group">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</xsl:template>

</xsl:stylesheet>
--

Martin Honnen
http://JavaScript.FAQTs.com/
Aug 14 '08 #3
MRe
With XSLT 1.0 you can solve such problems by processing sibling by
sibling, here is a sample stylesheet:
Ha, wow, very cool - this works prefect.

Thank you so much,
Plus extra thank you for the working sample,
And sorry for not stating it, XSLT 1.0 was what I needed

Thanks again,
Kind regards,
Eliott
>

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/test">
<xsl:copy>
<xsl:apply-templates select="node()[1]" mode="group"/>
</xsl:copy>
</xsl:template>

<xsl:template match="test/b" mode="group">
<c>
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1][self::b]"/>
</c>
<xsl:apply-templates
select="following-sibling::node()[not(self::b)][1]" mode="group"/>
</xsl:template>

<xsl:template match="test/b">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1][self::b]"/>
</xsl:template>

<xsl:template match="test/node()[not(self::b)]" mode="group">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</xsl:template>

</xsl:stylesheet>

--

Martin Honnen
http://JavaScript.FAQTs.com/
Aug 14 '08 #4
I will offer this solution, which uses just a key and the identity rule:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="kAllChildren" match="b" use=
"generate-id(
(preceding-sibling::node()
[not(self::b)][1]
|
parent::*[
*[1][self::b]
]
)
[last()]
)"/>

<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>

<xsl:template match="node()
[not(self::b)
and
following-sibling::node()[1][self::b]
]">
<xsl:copy-of select="."/>
<c>
<xsl:copy-of select="key('kAllChildren', generate-id())"/>
</c>
</xsl:template>

<xsl:template match="*[*[1][self::b]]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<c>
<xsl:copy-of select="key('kAllChildren', generate-id())"/>
</c>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>

<xsl:template match="b"/>
</xsl:stylesheet>

It produces the correct result even with source xml like this:

<test>
<b>0</b>
<a>1</a>
<a>2</a>
<b>3</b>
<b>4</b>
5
<b>6</b>
<b>7</b>
<b>8</b>
9
<d>
<e>10</e>
<b>11</b>
<e>12</e>
<f>13</f>
<b>14</b>
<b>15</b>
16
<b>17</b>
<b>18</b>
<b>19</b>
20
</d>
21
<b>22</b>
23
</test>

Cheers,
Dimitre Novatchev

"Martin Honnen" <ma*******@yahoo.dewrote in message
news:48***********************@newsspool4.arcor-online.net...
MRe wrote:
> The closest I've come to getting this is to call a recursive
template, passing child::*[1] initially, and following-sibling::*[1]
for each recursive step, and also passing a 'block' parameter that, if
false and <bis encountered, puts a <cin and sets block to true,
and if true and <ais encountered, puts a </cin and sets to false.
However, this won't work as it won't put a </cin if the last element
is a <b>.

With XSLT 1.0 you can solve such problems by processing sibling by
sibling, here is a sample stylesheet:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/test">
<xsl:copy>
<xsl:apply-templates select="node()[1]" mode="group"/>
</xsl:copy>
</xsl:template>

<xsl:template match="test/b" mode="group">
<c>
<xsl:copy-of select="."/>
<xsl:apply-templates
select="following-sibling::node()[1][self::b]"/>
</c>
<xsl:apply-templates
select="following-sibling::node()[not(self::b)][1]" mode="group"/>
</xsl:template>

<xsl:template match="test/b">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1][self::b]"/>
</xsl:template>

<xsl:template match="test/node()[not(self::b)]" mode="group">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</xsl:template>

</xsl:stylesheet>
--

Martin Honnen
http://JavaScript.FAQTs.com/

Aug 16 '08 #5

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

2 posts views Thread by Allan Bredahl | last post: by
1 post views Thread by Wil | last post: by
12 posts views Thread by Keith Chadwick | last post: by
reply views Thread by Christopher M. Lauer | last post: by
2 posts views Thread by darrel | last post: by
7 posts views Thread by One Handed Man \( OHM - Terry Burns \) | last post: by
1 post views Thread by jrwarwick | last post: by
1 post views Thread by Nick | last post: by
4 posts views Thread by gouranga | last post: by
5 posts views Thread by Gilgamesh | last post: by
reply views Thread by suresh191 | last post: by
4 posts views Thread by guiromero | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.