XSLT & String manipulation query

B

Bilal

Hello,
I'm trying to perform some string manipulations in my stylesheet and
have gotten stuck on the issue below so hopefully can elicit some useful
hints.

Namely, the problem is that I need to convert an unqualified Xpath to
a fully qualified Xpath in an identity transform, i.e.

/AAA/BBB/CCC/@DDD

converted to

/ns:AAA/ns:BBB/ns:CCC/@DDD

with a predefined NS prefix and using a string tokenizer (adopted from
http://www.xslt.com/html/xsl-list/2005-04/msg00031.html) which returns
the tokens as:

<token>AAA</token>
<token>BBB</token>
<token>CCC</token>
<token>@DDD</token>

I'm assigning to the variable 'tokens' in the following template, which
then tries to produce the fully qualified namespace:

<xsl:template name="qualifiedXpath">
<xsl:param name="unqualifiedXpath"/>
<!-- -->
<xsl:variable name="sampleUnqualifiedXpath"
select="'/AAA/BBB/CCC/@DDD'"/>
<!-- hardcoded namespace prefix -->
<xsl:variable name="prefixString" select="'dns:'"/>
<!-- hardcoded delimiter character -->
<xsl:variable name="slash" select="'/'"/>
<!-- Variable to contain the tokens -->
<xsl:variable name="tokens">
<!-- Calling tokenizer template-->
<xsl:call-template name="tokenizer">
<xsl:with-param name="string" select="$sampleUnqualifiedXpath"/>
<xsl:with-param name="delimiter" select="$slash"/>
</xsl:call-template>
</xsl:variable>
<!-- Variable to hold the qualified Xpath -->
<xsl:variable name="qualXpath">
<!-- Constructing the qualified Xpath-->
<!-- Iterate through the returned token nodes -->
<xsl:for-each select="$tokens/token">
<!-- Add delimiter-->
<xsl:value-of select="$slash"/>
<!-- Add prefix only when token is an element name i.e. doesn't have the
@ character -->
<xsl:if test="not(contains(.,'@'))">
<!-- Adding namespace prefix -->
<xsl:value-of select="$prefixString"/>
<!-- Add token's value -->
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- returning qualXpath variable -->
<xsl:value-of select="$qualXpath"/>
</xsl:template>

where the tokens' usage in the loop declaration
<xsl:for-each select="$tokens/token">

is causing a Result Tree Fragment (RTF) error; I've googled to find out
more about it but frankly don't understand the problem, and unsure what
an alternative solution would be and hence seeking Wisdom of The Elders!
:)

BTW, the code snippet above might be buggy (hopefully not too much) as
I've been stuck at this RTF error and hence unable to proceed. I suspect
the usage of contains(.,'@'),
where I intend to check the token node's value for a '@' character, is
bit fishy. :)

Many thanks!

Regards,

Bilal B.
 
R

Richard Tobin

Bilal said:
<xsl:variable name="tokens">
<!-- Calling tokenizer template-->
<xsl:call-template name="tokenizer">
<xsl:with-param name="string" select="$sampleUnqualifiedXpath"/>
<xsl:with-param name="delimiter" select="$slash"/>
</xsl:call-template>
</xsl:variable>

Here you construct a result-tree fragment - the value of the variable
"tokens" is not a nodeset from the original document, but a
constructed nodeset.

In XSLT 1.0 you can't do much with result-tree fragments. In particular
you can't do this sort of thing:
<xsl:for-each select="$tokens/token">

Many XSLT processors have an extension function that allows you to convert
a result-tree fragment into an ordinary nodeset. For example, the
exsl:node-set() function may be available, see

http://www.exslt.org/exsl/functions/node-set/

-- Richard
 
J

Joe Kesselman

As Richard implied, XSLT 2.0 (when it becomes official and more widely
available) removes that distinction between nodesets and result-tree
fragments.

If you don't want to rely on the extension function, the other 1.0
solution is to rewrite the tokenize-and-reconstruct process so it yields
the new string directly, rather than producing an RTF and then walking
that to produce the string. There are examples of recursive substring
replacement on the XSLT FAQ website, among many other places; it's a
fairly common idiom for functional languages like XSLT.
 
B

Bilal

Hi Richard & Joe,
Thanks for the explanation and suggestions. Joe, based on your
suggestion, I started exploring other solution and I think I've come
across a search-and-replace method in my XSLT cookbook which does this
job. Now time to understand and customize it! ;-)

BTW, I'm sure I'm not the only one with procedural language experience
who has trouble with this functional language; its seems like a whole
different beast!

Many thanks for the help guys!

Regards,

Bilal B.
 
D

Dimitre Novatchev

This transformation:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testmap="testmap"
exclude-result-prefixes="xsl testmap" <xsl:import href="str-map.xsl"/>

<!-- to be applied on any xml source -->

<testmap:testmap/>

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

<xsl:template match="/">
<xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>
<xsl:call-template name="str-map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pStr"
select="substring-before('/AAA/BBB/CCC/@DDD', '/@')"/>
</xsl:call-template>
<xsl:value-of select=
"concat('/@', substring-after('/AAA/BBB/CCC/@DDD', '/@'))"/>
</xsl:template>

<xsl:template name="double" match="testmap:*">
<xsl:param name="arg1"/>

<xsl:value-of select="$arg1"/>
<xsl:if test="$arg1 = '/'">
<xsl:value-of select="'ns:'"/>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

when applied on any source xml document (not used),

produces the wanted result:

/ns:AAA/ns:BBB/ns:CCC/@DDD


Cheers,
Dimitre Novatchev
 
D

Dimitre Novatchev

Of course, the imported stylesheet is from the EXSLT-version of FXSL1.x

When one has such a toolset, solving such problems takes just two minutes.

Cheers,
Dimitre Novatchev

Dimitre Novatchev said:
This transformation:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testmap="testmap"
exclude-result-prefixes="xsl testmap"<xsl:import href="str-map.xsl"/>

<!-- to be applied on any xml source -->

<testmap:testmap/>

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

<xsl:template match="/">
<xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>
<xsl:call-template name="str-map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pStr"
select="substring-before('/AAA/BBB/CCC/@DDD', '/@')"/>
</xsl:call-template>
<xsl:value-of select=
"concat('/@', substring-after('/AAA/BBB/CCC/@DDD', '/@'))"/>
</xsl:template>

<xsl:template name="double" match="testmap:*">
<xsl:param name="arg1"/>

<xsl:value-of select="$arg1"/>
<xsl:if test="$arg1 = '/'">
<xsl:value-of select="'ns:'"/>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

when applied on any source xml document (not used),

produces the wanted result:

/ns:AAA/ns:BBB/ns:CCC/@DDD


Cheers,
Dimitre Novatchev

Bilal said:
Hello,
I'm trying to perform some string manipulations in my stylesheet and
have gotten stuck on the issue below so hopefully can elicit some useful
hints.

Namely, the problem is that I need to convert an unqualified Xpath to
a fully qualified Xpath in an identity transform, i.e.

/AAA/BBB/CCC/@DDD

converted to

/ns:AAA/ns:BBB/ns:CCC/@DDD

with a predefined NS prefix and using a string tokenizer (adopted from
http://www.xslt.com/html/xsl-list/2005-04/msg00031.html) which returns
the tokens as:

<token>AAA</token>
<token>BBB</token>
<token>CCC</token>
<token>@DDD</token>

I'm assigning to the variable 'tokens' in the following template, which
then tries to produce the fully qualified namespace:

<xsl:template name="qualifiedXpath">
<xsl:param name="unqualifiedXpath"/>
<!-- -->
<xsl:variable name="sampleUnqualifiedXpath"
select="'/AAA/BBB/CCC/@DDD'"/>
<!-- hardcoded namespace prefix -->
<xsl:variable name="prefixString" select="'dns:'"/>
<!-- hardcoded delimiter character -->
<xsl:variable name="slash" select="'/'"/>
<!-- Variable to contain the tokens -->
<xsl:variable name="tokens">
<!-- Calling tokenizer template-->
<xsl:call-template name="tokenizer">
<xsl:with-param name="string" select="$sampleUnqualifiedXpath"/>
<xsl:with-param name="delimiter" select="$slash"/>
</xsl:call-template>
</xsl:variable>
<!-- Variable to hold the qualified Xpath -->
<xsl:variable name="qualXpath">
<!-- Constructing the qualified Xpath-->
<!-- Iterate through the returned token nodes -->
<xsl:for-each select="$tokens/token">
<!-- Add delimiter-->
<xsl:value-of select="$slash"/>
<!-- Add prefix only when token is an element name i.e. doesn't have the
@ character -->
<xsl:if test="not(contains(.,'@'))">
<!-- Adding namespace prefix -->
<xsl:value-of select="$prefixString"/>
<!-- Add token's value -->
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- returning qualXpath variable -->
<xsl:value-of select="$qualXpath"/>
</xsl:template>

where the tokens' usage in the loop declaration
<xsl:for-each select="$tokens/token">

is causing a Result Tree Fragment (RTF) error; I've googled to find out
more about it but frankly don't understand the problem, and unsure what
an alternative solution would be and hence seeking Wisdom of The Elders!
:)

BTW, the code snippet above might be buggy (hopefully not too much) as
I've been stuck at this RTF error and hence unable to proceed. I suspect
the usage of contains(.,'@'),
where I intend to check the token node's value for a '@' character, is
bit fishy. :)

Many thanks!

Regards,

Bilal B.
 
B

Bilal

Hi Dimitre,
Thanks for your input; your first reply seemed interesting, but as you
pointed out it using EXSLT, which I can't use just yet as I'm limited to
XSLT 1.0 only.

Regards,

Bilal B.
 
D

Dimitre Novatchev

Bilal said:
Hi Dimitre,
Thanks for your input; your first reply seemed interesting, but as you
pointed out it using EXSLT, which I can't use just yet as I'm limited to
XSLT 1.0 only.

Regards,

Bilal B.

Then you could use one of the three vendor-dependent versions of FXSL
1.x -- MSXML, Saxon or Xalan- dependent.

Cheers,
Dimitre Novatchev
 
D

Dimitre Novatchev

I understood him to say he was not ready yet to use EXSLT, not that his XSLT
processor didn't support it.

Cheers,
Dimitre Novatchev
 
B

Bilal

Yes, Dimitri rightly got what I intended! Although I am using Xalan-J,
but that is at the implementation end and I use XMLSpy for debugging
purposes (as thats the XML tool I'm most familiar with, and is used by
non-programmer colleagues :) and hence didn't want to rely on EXSLT etc.
as the latter doesn't support it AFAIK.

It's useful to know that Xalan does support EXSLT so I may use that
for future work.

Many thanks again!

Regards,

Bilal B.
I understood him to say he was not ready yet to use EXSLT, >not that
his XSLT processor didn't support it.
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top