Giving a flat document structure

J

J Kallay

Suppose that you have a document that looks like this:

<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>

and you wanted to transform it into something like this:
<item>
<list>
<item>
<item>
</list>
<item/>
<list>
<item/>
</list>
<item>

With string manipulation (regular expressions, for example), you could find
all items of type listitem that are preceded by something other than a
listitem (i.e. the first item in the list) and replace them with
<list><item/>. Similarly, you could find all listitems that are not
followed by another listitem (i.e. the list item in the list) and replace
them with <item/></list>. But how would this be done with XSLT?

The following (using 'firstlistitem' and 'lastlistitem' as shortcuts for the
query identifying items with preceding or following non-listitmes) wouldn't
work:
<xsl:template match=firstlistitem>
<list>
<xsl: value-of select=".">
</xsl:template>

<xsl:template match=lastlistitem>
<xsl: value-of select=".">
</list>
</xsl:template>

because it involves opening and closing the tag in different templates.
Instead, you'd need something like:

<xsl:template match=firstlistitem>
<list>
<xsl:for-each select="listitems between here and the next
'firstlistitem' ">
<xsl: value-of select=".">
</xsl:for-each>
</list>
</xsl:template>

It's the "listitems between here and the next 'firstlistitem' " that I'm
having trouble with.
 
S

shaun roe

"J Kallay said:
Suppose that you have a document that looks like this:

<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>

and you wanted to transform it into something like this:
<item>
<list>
<item>
<item>
</list>
<item/>
<list>
<item/>
</list>
<item>

ok, so I used this as input:
<newsgroupQuestion>
<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>
</newsgroupQuestion>

and this as the xsl:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:eek:utput indent="yes" method="xml"/>
<xsl:template match="item[@type='normal']">
<item/>
</xsl:template>
<xsl:template match="newsgroupQuestion">
<kallay>
<xsl:apply-templates/>
</kallay>
</xsl:template>
<xsl:template match="item[@type='listitem' and
preceding-sibling::item[1]/@type ='normal']">
<list>
<item/>
<xsl:call-template name="contiguous">
<xsl:with-param name="here"
select="following-sibling::item[1][@type='listitem']"/>
</xsl:call-template>
</list>
</xsl:template>
<xsl:template name="contiguous">
<xsl:param name="here"/>
<xsl:if test="$here/preceding-sibling::item[1]/@type='listitem'">
<item/>
<xsl:call-template name="contiguous">
<xsl:with-param name="here"
select="$here/following-sibling::item[1][@type='listitem']"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

to get this as the output:

<kallay>
<item/>
<list>
<item/>
<item/>
</list>

<item/>
<list>
<item/>
</list>
<item/>
</kallay>

is this what you need?

cheers
shaun
 
P

Pavel Lepin

shaun roe said:

[flat -> hierarchy grouping problem]
ok, so I used this as input:
<newsgroupQuestion>
<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>
</newsgroupQuestion>

and this as the xsl:

[solution]

This seems to break if you remove the first <item/> in the
source document.

I must say that this is a boring problem caused by godawful
quality of the source document. I would advise the OP to
violently bite off the head of whoever's generating said
document and be done with it.
 
S

shaun roe

Pavel Lepin <[email protected]> said:
shaun roe said:

[flat -> hierarchy grouping problem]
ok, so I used this as input:
<newsgroupQuestion>
<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>
</newsgroupQuestion>

and this as the xsl:

[solution]

This seems to break if you remove the first <item/> in the
source document.

I must say that this is a boring problem caused by godawful
quality of the source document. I would advise the OP to
violently bite off the head of whoever's generating said
document and be done with it.

:)
Now you're getting picky! ;-)

try replacing line 12 with

<xsl:template match="item[@type='listitem' and
(preceding-sibling::item[1]/@type !='listitem' or
name(preceding-sibling::*[1]) != 'item')]">


(in place of
<xsl:template match="item[@type='listitem' and
preceding-sibling::item[1]/@type ='normal']"> )

sometimes you've just got to live XFH... 'Xml From Hell'

'God grant me the serenity
to accept the things I cannot change;
the courage to change the things I can;
and the wisdom to know the difference.'
 
J

Joseph Kesselman

shaun said:
'God grant me the serenity
to accept the things I cannot change;
the courage to change the things I can;
and the wisdom to know the difference.'

And the strength to walk away from the ones I shouldn't get involved with.
 
D

Dimitre Novatchev

J Kallay said:
Suppose that you have a document that looks like this:

<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>

and you wanted to transform it into something like this:
<item>
<list>
<item>
<item>
</list>
<item/>
<list>
<item/>
</list>
<item>

With string manipulation (regular expressions, for example), you could
find all items of type listitem that are preceded by something other than
a listitem (i.e. the first item in the list) and replace them with
<list><item/>. Similarly, you could find all listitems that are not
followed by another listitem (i.e. the list item in the list) and replace
them with <item/></list>. But how would this be done with XSLT?

The following (using 'firstlistitem' and 'lastlistitem' as shortcuts for
the query identifying items with preceding or following non-listitmes)
wouldn't work:
<xsl:template match=firstlistitem>
<list>
<xsl: value-of select=".">
</xsl:template>

<xsl:template match=lastlistitem>
<xsl: value-of select=".">
</list>
</xsl:template>

because it involves opening and closing the tag in different templates.
Instead, you'd need something like:

<xsl:template match=firstlistitem>
<list>
<xsl:for-each select="listitems between here and the next
'firstlistitem' ">
<xsl: value-of select=".">
</xsl:for-each>
</list>
</xsl:template>

It's the "listitems between here and the next 'firstlistitem' " that I'm
having trouble with.



This transformation:

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

<xsl:key name="kLItems" match="item[@type='listitem']"
use="generate-id(preceding-sibling::item
[not(@type = 'listitem')]
[1]
)"/>

<xsl:template match="t">
<t>
<xsl:apply-templates select=
"item[@type='normal']
|
item[not(preceding-sibling::*[1]/@type = 'listitem')]"/>
</t>
</xsl:template>

<xsl:template match=
"item[@type='listitem'
and
not(preceding-sibling::*[1]
[@type = 'listitem']
)
]">
<list>
<item/>
<xsl:apply-templates select=
"key('kLItems', generate-id(preceding-sibling::*[1]))
[position() > 1]"/>
</list>
</xsl:template>

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

when applied on the provided xml document:

<t>
<item type="normal"/>
<item type="listitem"/>
<item type="listitem"/>
<item type="normal"/>
<item type="listitem"/>
<item type="normal"/>
</t>

Produces the wanted result:

<t>
<item />
<list>
<item />
<item />
</list>
<item />
<list>
<item />
</list>
<item />
</t>


Cheers,
Dimitre Novatchev
 

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

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top