xsl-list
[Top] [All Lists]

RE: [xsl] Combining consecutive siblings

2006-06-13 02:19:43
Hi Andrew

Thank you very much. Just a few concluding notes:

1.
----
<xsl:template match="CELL">
<td><xsl:apply-templates select="SB"/></td>
</xsl:template>
----

I know my example didn't show it, but in Real Life there are an awful lot of
things in CELLs, and every so often amongst them there is a run of SB
elements which I want to collapse in this way. So the above is too simple,
unfortunately. (There also may be two runs of SB with some other intervening
element, as well.)

2.
----
<xsl:template match="SB[preceding-sibling::*[1][self::SB]]" priority="2"/>
----

This I have added - it should kick in for any SB selected (without a mode)
whether or not 'select="SB"' was specified on the apply-templates node,
right?

3.
When I experimented by interspersing random elements amongst the SB runs, I
found that they were each appearing twice. I see now, I think, that Dr Kay's
templates presumed that the "higher-order" template was only selecting the
first <B> child, hence the need to include
----
<xsl:apply-templates select="following-sibling::*[not(self::b)][1]"/>
----
(or [not(self::SB)] in my case) after the closing <B> in the main template.
As my "higher-order" template (I don't know what the canonical term is,
sorry) is simply doing a general <apply-templates /> then non-SB children
are pulled in anyway. So I dropped that line as well.

On the samples I have tried since making these changes, it seems now to be
doing what I want.

Thanks again.

Cheers
Trevor

-----Original Message-----
From: andrew welch [mailto:andrew(_dot_)j(_dot_)welch(_at_)gmail(_dot_)com] 
Sent: Tuesday, 13 June 2006 8:08 p.m.
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: Re: [xsl] Combining consecutive siblings

In your CELL matching template you are applying-templates to all SB
children, rather than just the first SB child in each sequence of
SB's.

Add a no-op template for any SB that isn't the first in the sequence, eg:

<xsl:template match="CELL">
<td><xsl:apply-templates select="SB"/></td>
</xsl:template>

<xsl:template match="SB[preceding-sibling::*[1][self::SB]]" priority="2"/>


cheers
andrew

On 6/13/06, Trevor Nicholls <trevor(_at_)castingthevoid(_dot_)com> wrote:
Hello

In some contexts the XML I am processing contains a run of consecutive
elements which I want to merge into a single element in the output XML. I
have adapted a technique which I found in the archives (it was called
"grouping consecutive elements" then, and didn't do precisely what I
wanted,
but seemed to be the right approach).

Here's a cut down piece of XML which highlights the problem:
--------
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Body>If you add this to the enquiry:</Body>
<TABLE><ROW><CELL>
<SB>where </SB>
<SB>  <File>client</File> has <Field>country</Field>="NZ"</SB>
<SB>  and <Field>city</Field>="WELLINGTON"</SB>
<SB>  and <Field>name</Field> matches (plum* ~plumlee*)</SB>
<SB>  and val(<Field>street</Field>[1,pos(" ",<Field>street</Field>)-1])
&gt; 0</SB>
<SB>list</SB>
<SB>  <File>client</File>:<Field>name</Field> <Field>address</Field></SB>
</CELL></ROW></TABLE>
</root>
--------

The requirement is to collapse all the consecutive <SB> elements into a
single <syntax> element, with newlines in the output reflecting the
multiple
<SB> nodes, and retaining any lower level structure.

This is the XSL I have arrived at:
--------
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:xs="http://www.w3.org/2001/XMLSchema";>
<xsl:output method="xml" encoding="UTF-8"/>

<xsl:template match="root">
<document><xsl:apply-templates /></document>
</xsl:template>

<xsl:template match="Body">
<xsl:call-template name="nl" />
<para><xsl:apply-templates /></para>
</xsl:template>

<xsl:template match="TABLE">
<xsl:call-template name="nl" />
<xsl:element name="table">
<tbody><xsl:apply-templates select="ROW[CELL]" /></tbody>
</xsl:element>
</xsl:template>

<xsl:template match="ROW">
<tr><xsl:apply-templates /></tr>
</xsl:template>

<xsl:template match="CELL">
<td><xsl:apply-templates /></td>
</xsl:template>

<xsl:template match="SB">
<xsl:call-template name="nl" />
<syntax>
<xsl:apply-templates select="child::node()" mode="syn"/>
<xsl:apply-templates select="following-sibling::*[1][self::SB]"
mode="more"
/>
</syntax>
<xsl:apply-templates select="following-sibling::*[not(self::SB)][1]" />
</xsl:template>

<xsl:template match="SB" mode="more">
<xsl:call-template name="nl" />
<xsl:apply-templates select="child::node()" mode="syn"/>
<xsl:apply-templates select="following-sibling::*[1][self::SB]"
mode="more"
/>
</xsl:template>

<xsl:template match="File | Field" mode="syn">
<f><xsl:apply-templates /></f>
</xsl:template>

<xsl:template name="nl"><xsl:text>&#xa;</xsl:text></xsl:template>

</xsl:stylesheet>
--------

The original templates were posted (by Dr Kay, to whom thanks) as an
example
of processing elements using "horizontal" recursion: I'll include them
here
because someone may instantly see where my adaptation is in error:

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

<xsl:template match="b" mode="more">
  <xsl:copy-of select="."/>
  <xsl:apply-templates
    select="following-sibling::*[1][self::b]"
    mode="more"/>
</xsl:template>
--------

Apart from the obvious edits due to my input repeat being <SB> and my
output
"container" being <syntax>, I had to make two other changes: this original
template copied the repeated elements (copy-of select=".") which gave me
output XML of <syntax><SB>..</SB><SB>..</SB><SB>..</SB></syntax> and not
<syntax>...</syntax>, secondly when I replaced the copy-of with
<apply-templates select=child::node() /> to retain the low-level structure
I
found that the mode="more" was being carried through, hence the mode="syn"
in the stylesheet above.

The resultant output is wrong because instead of giving me one syntax
element containing the content of all the consecutive SB elements, I have
as
many syntax elements as SBs, gradually reducing in length, viz:

--------
<?xml version="1.0" encoding="UTF-8"?><document
xmlns:xs="http://www.w3.org/2001/XMLSchema";>
<para>If you add this to the enquiry:</para>
<table><tbody><tr><td>
<syntax>where
<f>client</f> has <f>country</f>="NZ"
  and <f>city</f>="WELLINGTON"
  and <f>name</f> matches (plum* ~plumlee*)
  and val(<f>street</f>[1,pos(" ",<f>street</f>)-1]) &gt; 0
list
<f>client</f>:<f>name</f><f>address</f></syntax>
<syntax><f>client</f> has <f>country</f>="NZ"
  and <f>city</f>="WELLINGTON"
  and <f>name</f> matches (plum* ~plumlee*)
  and val(<f>street</f>[1,pos(" ",<f>street</f>)-1]) &gt; 0
list
<f>client</f>:<f>name</f><f>address</f></syntax>
<syntax>  and <f>city</f>="WELLINGTON"
  and <f>name</f> matches (plum* ~plumlee*)
  and val(<f>street</f>[1,pos(" ",<f>street</f>)-1]) &gt; 0
list
<f>client</f>:<f>name</f><f>address</f></syntax>
<syntax>  and <f>name</f> matches (plum* ~plumlee*)
  and val(<f>street</f>[1,pos(" ",<f>street</f>)-1]) &gt; 0
list
<f>client</f>:<f>name</f><f>address</f></syntax>
<syntax>  and val(<f>street</f>[1,pos(" ",<f>street</f>)-1]) &gt; 0
list
<f>client</f>:<f>name</f><f>address</f></syntax>
<syntax>list
<f>client</f>:<f>name</f><f>address</f></syntax>
<syntax><f>client</f>:<f>name</f><f>address</f></syntax>
</td></tr></tbody></table></document>
--------

What am I doing wrong? Can anyone help?

Cheers
Trevor


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>