Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

recursion problem

3 views
Skip to first unread message

Koen De Wolf

unread,
Dec 3, 2002, 12:20:48 PM12/3/02
to
hello,

I have a problem with recursion and the with-param feature

let's say this is my (much simplified) XML file:

<YY name="root">
<XX name="AA" type="BB"/>
<TYPE name="BB">
<XX name="CC" type="DD"/>
<XX name="CC" type="EE"/>
</TYPE>
<TYPE name="DD">
<XX name="FF" type="EE"/>
</TYPE>
<TYPE name="EE">
<XX name="GG"/>
</TYPE>
</YY>


I want to transform it to

<XX name="GG" path="'/root/AA/CC/FF/GG'"/>
<XX name="GG" path="'/root/AA/CC/GG'"/>

The structure of the new XML-file is not that dificult to achieve:

<xsl:template match="/">
<xsl:apply-templates select="/YY/XX"/>
</xsl:template>

<xsl:template match="XX">
<xsl:param name="type" select="@type"/>
<xsl:choose>
<xsl:when test="string-length(@type)=0">
<XX>
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
</XX>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/*//*[@name=$type]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

But how can create the path attribute? I've tried with parameters, but that didn't seem to work:

<xsl:template match="XX">
<xsl:param name="path"/>
<xsl:param name="type" select="@type"/>
<xsl:choose>
<xsl:when test="string-length(@type)=0">
<XX>
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
<xsl:attribute name="path"><xsl:value-of select="$path"/></xsl:attribute>
</XX>
</xsl:when>
<xsl:otherwise>
<xsl:param name="name" select="string(@name)"/>
<xsl:param name="conc" select="concat($path,'/',$name)"/>
<xsl:apply-templates select="/*//*[@name=$type]">
<xsl:with-param name="path" select="$conc"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

tx for helping.


kind regards

Koen

--
---------------------------------------------------------
In a trainstation, the train stops. In a workstation ....

Dimitre Novatchev

unread,
Dec 3, 2002, 4:40:08 PM12/3/02
to

"Koen De Wolf" <kode...@news.rug.ac.be> wrote in message
news:asip5g$mpm$1...@gaudi2.rug.ac.be...

Hi Koen,

Please, find below the solution to your problem. It consists of two passes
and uses the xx:node-set() extension function to convert the RTF produced by
the first pass to a normal node-set.

Pass1: Converts your source xml to a tree. This temporary tree (for your
source xml) will be:

<root>
<AA>

<CC>
<FF>
<GG />
</FF>
</CC>

<CC>
<GG />
</CC>

</AA>
</root>


The second pass produces the path for all elements in the above tree, which
do not have any child-element. I use here a ready template of mine for
producing the path for any type of node (root, element, text, comment,
processing instruction, attribute and namespace):

http://www.topxml.com/code/default.asp?p=3&id=v20010323001030

Obviously in your case this template can be greatly simplified and
shortened, because you'll need the path only of element nodes.

Here's the transformation:
----------------------------
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vendor="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="vendor">

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="/">
<xsl:variable name="vrtfTree">
<xsl:apply-templates select="YY"/>
</xsl:variable>

<!--<xsl:copy-of select="vendor:node-set($vrtfTree)"/>-->

<xsl:for-each select="vendor:node-set($vrtfTree)//*[not(*)]">
<xsl:variable name="vaPath">
<xsl:call-template name="getPath">
<xsl:with-param name="theNode" select="."/>
</xsl:call-template>
</xsl:variable>

<XX name="{name()}" path="{$vaPath}"/>
</xsl:for-each>
</xsl:template>

<xsl:template match="/YY">
<xsl:element name="{@name}">
<xsl:apply-templates select="XX"/>
</xsl:element>
</xsl:template>

<xsl:template match="XX">
<xsl:element name="{@name}">
<xsl:apply-templates
select="following::TYPE[@name = current()/@type]/XX"/>
</xsl:element>
</xsl:template>


<xsl:template name="getPath">
<xsl:param name="theNode" select="/.."/>
<xsl:for-each select="$theNode |
$theNode/ancestor-or-self::node()[..]">
<xsl:element name="slash">/</xsl:element>
<xsl:choose>
<xsl:when test="self::*">
<xsl:element name="nodeName">
<xsl:value-of select="name()"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::*[name(current()) =
name()])"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::*[name(current()) =
name()])"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition + 1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:otherwise> <!-- This node is not an element -->
<xsl:choose>
<xsl:when test="count(. | ../@*) = count(../@*)">
<!-- Attribute -->
<xsl:element name="nodeName">
<xsl:value-of select="concat('@',name())"/>
</xsl:element>
</xsl:when>
<xsl:when test="self::text()"> <!-- Text -->
<xsl:element name="nodeName">
<xsl:value-of select="'text()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::text())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::text())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition + 1,
']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<!-- Processing Instruction -->
<xsl:element name="nodeName">
<xsl:value-of select="'processing-instruction()'"/>
<xsl:variable name="thisPosition"

select="count(preceding-sibling::processing-instruction())"/>
<xsl:variable name="numFollowing"

select="count(following-sibling::processing-instruction())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition + 1,
']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="self::comment()"> <!-- Comment -->
<xsl:element name="nodeName">
<xsl:value-of select="'comment()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::comment())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::comment())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition + 1,
']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<!-- Namespace: -->
<xsl:when test="count(. | ../namespace::*) =
count(../namespace::*)">

<xsl:variable name="apos">'</xsl:variable>
<xsl:element name="nodeName">
<xsl:value-of select="concat('namespace::*',
'[local-name()
= ',
$apos,
local-name(), $apos, ']')"/>

</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>


</xsl:stylesheet>

When applied on your source xml the result is:

<XX name="GG" path="/root/AA/CC[1]/FF/GG" />
<XX name="GG" path="/root/AA/CC[2]/GG" />


Hope this helped.


=====
Cheers,

Dimitre Novatchev.
http://fxsl.sourceforge.net/ -- the home of FXSL


Koen De Wolf

unread,
Dec 4, 2002, 10:55:12 AM12/4/02
to
I'm gonna check it out right away.

Thank you very much!!

Koen

Koen De Wolf (kode...@news.rug.ac.be) wrote:
: hello,

Koen De Wolf

unread,
Dec 4, 2002, 11:13:52 AM12/4/02
to

I encountered a problem, it works fine with msxml, but it should also work with any other XSLT engine :(

I try to solve it on my own, thanx for giving me support!

Koen

Koen De Wolf (kode...@news.rug.ac.be) wrote:

: I'm gonna check it out right away.

Mike Brown

unread,
Dec 6, 2002, 5:18:10 AM12/6/02
to
> I encountered a problem, it works fine with msxml,
> but it should also work with any other XSLT engine :(

Nearly all XSLT processors have an extension function that converts a result
tree fragment to a node-set. Several of them now use the EXSLT version (see
http://exslt.org/). You should use function-available() if you need to make
a more portable stylesheet. Something like this...

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

xmlns:exsl="http://exslt.org/common"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:saxon="http://icl.com/saxon"
xmlns:xalan="http://xml.apache.org/xalan"
exclude-result-prefixes="exsl msxsl saxon xalan">

...

<!-- result tree fragment example -->
<xsl:variable name="rtf">
<greeting xml:lang="en">hello world</greeting>
</xsl:variable>

<xsl:choose>
<xsl:when test="function-available('exsl:node-set')">
<xsl:call-template name="foo">
<xsl:with-param name="nodes" select="exsl:node-set($rtf)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="function-available('msxsl:node-set')">
<xsl:call-template name="foo">
<xsl:with-param name="nodes" select="msxsl:node-set($rtf)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="function-available('xalan:node-set')">
<xsl:call-template name="foo">
<xsl:with-param name="nodes" select="xalan:nodeSet($rtf)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="function-available('saxon:node-set')">
<xsl:call-template name="foo">
<xsl:with-param name="nodes" select="saxon:node-set($rtf)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">No result tree fragment to node-set
conversion available!</xsl:message>
</xsl:otherwise>
</xsl:choose>

...

<xsl:template name="foo">
<xsl:param name="nodes"/>
<!-- everything we need to do with $nodes goes here -->
<xsl:value-of select="$nodes/greeting"/>
</xsl:template>

...

</xsl:stylesheet>


Koen De Wolf

unread,
Dec 6, 2002, 6:02:19 AM12/6/02
to
tx,

I will try this next weekend!

Koen
"Mike Brown" <mi...@skew.org> schreef in bericht
news:3df07962$1...@omega.dimensional.com...

0 new messages