Firstly, as Andrew replied: it's really useful to declare the types of
parameters and expected result in xsl:function.
Secondly, I would expect this function to operate on strings. A function
that operates on strings shouldn't usually contain an xsl:value-of
instruction, which creates a text node - there should be no need to have any
nodes involved. Use xsl:sequence in place of xsl:value-of.
Finally, and I think this is the cause of your error, if you write a
function (or indeed a template) that creates multiple strings (or multiple
text nodes) then they are not automatically concatenated: the result of the
function or template is a sequence of strings (or text nodes). Automatic
concatenation of text nodes happens only when they form part of the content
of an element constructor or document constructor. You need to concatenate
the strings "by hand" using concat() or string-join().
Sometimes I use
<xsl:function name="x" as="xs:string">
<xsl:value-of>
<xsl:sequence select="'one string'"/>
<xsl:sequence select="'another string'"/>
</xsl:value-of>
</xsl:function>
as a way of achieving the concatenation. However, combining the strings into
a text node which is then atomized to a string is not very clean
conceptually, and you need to watch out for space separators being added.
Michael Kay
http://www.saxonica.com/
-----Original Message-----
From: Mario Madunic [mailto:hajduk(_at_)imag(_dot_)net]
Sent: 06 May 2008 17:14
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [xsl] help with recursive function
Hi,
I'm having a problem with a recursive function in 2.0.
I'm iterating through the value of a node that is tokenized
on spaces. I normalize the 'word' to lower-case and remove an
's if it exists. Then check it against a library xml file
(spellingBee.xml), if the 'word' isn't listed then just
upper-case the first letter and leaving the rest as is;
otherwise replace the 'word' with the corrected spelling in
the spellingBee.xml. Finish by concatenating new 'word' with
the value of g_AposS. That was simple enough to create but I
decided to take it one step further and check the current 'word'
for a simple set of delimiters (-/\); doing this to keep down
the number of entries in spellingBee.xml. Then start ripping
it apart into two pieces: before the delimiter and after the
delimiter. The 'word' before the delimiter would be checked
against spellingBee.xml and the rules mentioned above would
be applied.
The bit after the delimiter would be tossed back into the
function by calling the function again. That is when I
receive the following error message:
[xslt]
[xslt]
[xslt] original headline :: MEET THE BEATLES
ABC's-A.B.C./A.b.c.'s-ABC\abc's\nbc/n.b.c.'s
[xslt]
[xslt]
[xslt] e:\XSLT\cleanUp.xsl:259: Fatal Error! A sequence of
more than one item is not allowed as the first argument of
concat() [xslt] Failed to process null
input XML node
<head>MEET THE BEATLES
ABC's-A.B.C./A.b.c.'s-ABC\abc's\nbc/n.b.c.'s</head>
called in cleanUp.xsl
<xsl:variable name="l_TokenizedString"
select="tokenize($l_HeadTemp, ' ')" />
<xsl:for-each select="$l_TokenizedString">
<!-- line 259 mentioned in error message -->
<xsl:value-of select="concat(f:f_UpperLowerCaseFix(.), if
(not(position() =
last())) then ' ' else '')" />
</xsl:for-each>
spellingBee.xml looks like the following
<fixCase>
<word>
<lowerCase>abc</lowerCase>
<corrected>ABC</corrected>
</word>
<word>
<lowerCase>a.b.c.</lowerCase>
<corrected>A.B.C.</corrected>
</word>
</fixCase>
The function itself
<xsl:function name="f:f_UpperLowerCaseFix">
<xsl:param name="p_String" />
<!-- this variable actually exists in a global variables
file and is only defined here for posting on XSLT mailing list -->
<xsl:variable name="g_AposS">'s</xsl:variable>
<xsl:variable name="l_Delimiter">
<xsl:choose>
<xsl:when test="contains($p_String,
'-')"><xsl:value-of select="'-'"
/></xsl:when>
<xsl:when test="contains($p_String,
'/')"><xsl:value-of select="'/'"
/></xsl:when>
<xsl:when test="contains($p_String,
'\')"><xsl:value-of select="'\'"
/></xsl:when>
<xsl:otherwise><xsl:value-of select="'none'"
/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$l_Delimiter = 'none'">
<xsl:variable name="l_String" select="lower-case(if
(ends-with($p_String, $g_AposS)) then
substring-before($p_String, $g_AposS) else $p_String)" />
<xsl:variable name="l_AposS" select="if
(ends-with($p_String, $g_AposS)) then $g_AposS else ''" />
<xsl:choose>
<xsl:when
test="doc('../../supXML/spellingBee.xml')/fixCase//lowerCase[.
= $l_String]">
<xsl:value-of
select="concat(doc('../../supXML/spellingBee.xml')/fixCase/wor
d//corrected[preceding-sibling::*[.
= $l_String]], $l_AposS)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of
select="concat(upper-case(substring($l_String, 1, 1)),
substring($l_String, 2, string-length($l_String)), $l_AposS)" />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="l_StringBeforeDelimiter"
select="substring-before(lower-case($p_String), $l_Delimiter)" />
<xsl:variable name="l_StringAfterDelimiter"
select="substring-after(lower-case($p_String), $l_Delimiter)" />
<xsl:variable name="l_String" select="if
(ends-with($l_StringBeforeDelimiter, $g_AposS)) then
substring-before($l_StringBeforeDelimiter, $g_AposS) else
$l_StringBeforeDelimiter" />
<xsl:variable name="l_AposS" select="if
(ends-with($l_StringBeforeDelimiter, $g_AposS)) then $g_AposS
else ''" />
<xsl:choose>
<xsl:when
test="doc('../../supXML/spellingBee.xml')/fixCase//lowerCase[.
= $l_String]">
<xsl:value-of
select="concat(doc('../../supXML/spellingBee.xml')/fixCase/wor
d//corrected[preceding-sibling::*[.
= $l_String]], $l_AposS, $l_Delimiter)" />
<xsl:value-of
select="f:f_UpperLowerCaseFix($l_StringAfterDelimiter)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of
select="concat(upper-case(substring($l_String, 1, 1)),
substring($l_String, 2, string-length($l_String)), $l_AposS,
$l_Delimiter)" />
<xsl:value-of
select="f:f_UpperLowerCaseFix($l_StringAfterDelimiter)" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
The desired output from the sample node above should look like
input: MEET THE BEATLES ABC's-A.B.C./A.b.c.'s-ABC\abc's\nbc/n.b.c.'s
output: Meet The Beatles ABC's-A.B.C./A.B.C.'s-ABC\ABC's\nbc/n.b.c.'s
Any help will be appreciated.
Marijan (Mario) Madunic
--~------------------------------------------------------------------
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>
--~--
--~------------------------------------------------------------------
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>
--~--