Help | Site Map
Connecting Tech Pros Worldwide
 
 
LinkBack Thread Tools
  #1  
Old August 14th, 2008, 01:35 PM
MRe
Guest
 
Posts: n/a
Default XSLT - Sub-grouping in mixed nodes

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
  #2  
Old August 14th, 2008, 02:25 PM
Martin Honnen
Guest
 
Posts: n/a
Default Re: XSLT - Sub-grouping in mixed nodes

MRe wrote:
Quote:
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/
  #3  
Old August 14th, 2008, 02:25 PM
Martin Honnen
Guest
 
Posts: n/a
Default Re: XSLT - Sub-grouping in mixed nodes

MRe wrote:
Quote:
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/
  #4  
Old August 14th, 2008, 04:45 PM
MRe
Guest
 
Posts: n/a
Default Re: XSLT - Sub-grouping in mixed nodes

With XSLT 1.0 you can solve such problems by processing sibling by
Quote:
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
Quote:
>
>
<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/
  #5  
Old August 16th, 2008, 04:05 PM
Dimitre Novatchev
Guest
 
Posts: n/a
Default Re: XSLT - Sub-grouping in mixed nodes

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" <mahotrash@yahoo.dewrote in message
news:48a42bba$0$20699$9b4e6d93@newsspool4.arcor-online.net...
Quote:
MRe wrote:
>
Quote:
> 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/

 

Bookmarks

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are Off
[IMG] code is Off
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On

What is Bytes?

We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights. Get the best answers to your questions from over network members.
Post your question now . . .
It's fast and it's free

Popular Articles