I have to repeat the following XSLT snippet like 100 times and I would like it to be as small as possible. Is there a way to make an equivalent XSLT snippet that is shorter?
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:choose>
<xslo:when test="string($myVariable) != 'NaN'">
<xslo:text>1</xslo:text>
</xslo:when>
<xslo:otherwise>
<xslo:text>0</xslo:text>
</xslo:otherwise>
</xslo:choose>
I'm basically setting the state of a checkbox based on whether or not a value exists in //This/that/anotherthing in the source xml.
Can be XSLT 1.0 or XSLT 2.0, doesn't matter.
You can use an if instead of xsl:choose (XSLT 2.0 only)...
<xsl:value-of select="if (string(number(//This/that/anotherthing)) = 'NaN') then 0 else 1"/>
I also dropped the xsl:variable, but if you need it for some other reason, you can put it back.
You could also create a function...
<xsl:function name="local:isNumber">
<xsl:param name="context"/>
<xsl:value-of select="if (string(number($context)) = 'NaN') then 0 else 1"/>
</xsl:function>
usage...
<xsl:value-of select="local:isNumber(//This/that/anotherthing)"/>
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:value-of select="number(boolean($myVariable))"/>
If I understand the purpose correctly - that is return 1 if the value in question can be successfully expressed as a number, 0 otherwise - then I believe:
<xsl:value-of select="number(//This/that/anotherthing castable as xs:double)"/>
would be the most straightforward way (in XSLT 2.0) to achieve it.
Edit
In view of your change of purpose:
I'm basically setting the state of a checkbox based on whether or not
a value exists in //This/that/anotherthing
That's even simpler:
<xsl:value-of select="number(boolean(string(//This/that/anotherthing)))"/>
Related
I have problem with my code in XSLT with if. I am using key function and there I find out if something is in the key or not.
<xsl:key name="hlp" match="help" use="#id" />
...
<xsl:if test="key('hlp', #some_id) !=''">
...
</xsl:if>
That is correct it gives me what I want but how I can make opposite condition that #some_id isn´t in key hlp... I mean:
<xsl:if test="key('hlp', #some_id) <!--is equal--> ''">
...
</xsl:if>
Is there something like that in XSLT/XPath?
When you call key('x', 'y'), the result is the set of nodes in which the key is equal to 'y'. You can test whether a node-set is empty using the empty() function (in XSLT 2.0) or the not() function in XSLT 1.0:
<xsl:if test="not(key('x', 'y'))" version="1.0">...</xsl:if>
<xsl:if test="empty(key('x', 'y'))" version="2.0">...</xsl:if>
or for the inverse test (to test if something was found):
<xsl:if test="key('x', 'y')" version="1.0">...</xsl:if>
<xsl:if test="exists(key('x', 'y'))" version="2.0">...</xsl:if>
Testing by comparing the result against a string is wrong. The tests key('x','y')='' and key('x','y')!='' will both return false if the result of the key() function is an empty node-set; conversely, if the key() function selects two nodes, one with content and the other without, then both tests will return true.
<xsl:key name="hlp" match="help" use="#id" />
...
<xsl:if test="key('hlp', #some_id) !=''">
...
</xsl:if>
As already pointed by Michael Kay, avoid using the != operator unless truly knowing what it does.
This aside (and the fact that the key() function returns a node-set), it is more in the spirit of XSLT to write the above as:
<xsl:apply-templates select="key('hlp', #some_id)"/>
but how I can make opposite condition that #some_id isn´t in key
hlp... I mean:
<xsl:if test="key('hlp', #some_id) <!--is equal--> ''">
...
</xsl:if> ```
Is there something like that in XSLT/XPath?
Again, in the spirit of XSLT I recommend using code like this:
<xsl:apply-templates select="/*[not(key('hlp', #some_id))]" mode="not-found"/>
In the select expression above one can substitute /* with any existing node in the document -- if this really matters.
I am writing an XSLT to transform an XML: I am not experienced in Xpath, and my XML is too big to post.
But here is my problem:
This code: works fine:
<cac:testmathieu>
<xsl:variable name="lineAmount2" select="body:LineAmount * -1" />
<xsl:value-of select="../body:TaxTrans[body:TaxBaseAmount=$lineAmount2]/body:TaxAmount"/>
</cac:testmathieu>
Then, why is the code below not working, when it's used right below the above code:
<xsl:call-template name ="TaxTotalLine">
<xsl:with-param name="TaxAmount" select="../body:TaxTrans[body:TaxBaseAmount=body:LineAmount*-1]/body:TaxAmount"></xsl:with-param>
</xsl:call-template>
The second piece of code is just a merge of of the code in the first example, or am I mistaken?
I think you want
<xsl:call-template name ="TaxTotalLine">
<xsl:with-param name="TaxAmount" select="../body:TaxTrans[body:TaxBaseAmount = current()/body:LineAmount*-1]/body:TaxAmount"></xsl:with-param>
</xsl:call-template>
to compare the LineAmount of the currently processed node (e.g. any outer template or for-each) to the TaxBaseAmout of the TaxTrans element you have applied the predicate in square brackets to.
thanks for the response.
It was acutally a combination of the "current()" and the brackets.
Much appreciated !
Kind regards
Mathieu
I know the following question is a little bit of beginners but I need your help to understand a basic concept.
I would like to say first that I'm a XSLT programmer for 3 years and yet there are some new and quite basics things I've been learning here I never knew (In my job anyone learns how to program alone, there is no course involved).
My question is:
What is the usage of xsl:sequence?
I have been using xsl:copy-of in order to copy node as is, xsl:apply-templates in order to modifiy nodes I selected and value-of for simple text.
I never had the necessity using xsl:sequence. I would appreciate if someone can show me an example of xsl:sequence usage which is preferred or cannot be achieved without the ones I noted above.
One more thing, I have read about the xsl:sequence definition of course, but I couldn't infer how it is useful.
<xsl:sequence> on an atomic value (or sequence of atomic values) is the same as <xsl:copy-of> both just return a copy of their input. The difference comes when you consider nodes.
If $n is a single element node, eg as defined by something like
<xsl:variable name="n" select="/html"/>
Then
<xsl:copy-of select="$n"/>
Returns a copy of the node, it has the same name and child structure but it is a new node with a new identity (and no parent).
<xsl:sequence select="$n"/>
Returns the node $n, The node returned has the same parent as $n and is equal to it by the is Xpath operator.
The difference is almost entirely masked in traditional (XSLT 1 style) template usage as you never get access to the result of either operation the result of the constructor is implicitly copied to the output tree so the fact that xsl:sequence doesn't make a copy is masked.
<xsl:template match="a">
<x>
<xsl:sequence select="$n"/>
</x>
</xsl:template>
is the same as
<xsl:template match="a">
<x>
<xsl:copy-of select="$n"/>
</x>
</xsl:template>
Both make a new element node and copy the result of the content as children of the new node x.
However the difference is quickly seen if you use functions.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="data:,f">
<xsl:variable name="s">
<x>hello</x>
</xsl:variable>
<xsl:template name="main">
::
:: <xsl:value-of select="$s/x is f:s($s/x)"/>
:: <xsl:value-of select="$s/x is f:c($s/x)"/>
::
:: <xsl:value-of select="count(f:s($s/x)/..)"/>
:: <xsl:value-of select="count(f:c($s/x)/..)"/>
::
</xsl:template>
<xsl:function name="f:s">
<xsl:param name="x"/>
<xsl:sequence select="$x"/>
</xsl:function>
<xsl:function name="f:c">
<xsl:param name="x"/>
<xsl:copy-of select="$x"/>
</xsl:function>
</xsl:stylesheet>
Produces
$ saxon9 -it main seq.xsl
<?xml version="1.0" encoding="UTF-8"?>
::
:: true
:: false
::
:: 1
:: 0
::
Here the results of xsl:sequence and xsl:copy-of are radically different.
The most common use case for xsl:sequence is to return a result from xsl:function.
<xsl:function name="f:get-customers">
<xsl:sequence select="$input-doc//customer"/>
</xsl:function>
But it can also be handy in other contexts, for example
<xsl:variable name="x" as="element()*">
<xsl:choose>
<xsl:when test="$something">
<xsl:sequence select="//customer"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="//supplier"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
The key thing here is that it returns references to the original nodes, it doesn't make new copies.
Well to return a value of a certain type you use xsl:sequence as xsl:value-of despite its name always creates a text node (since XSLT 1.0).
So in a function body you use
<xsl:sequence select="42"/>
to return an xs:integer value, you would use
<xsl:sequence select="'foo'"/>
to return an xs:string value and
<xsl:sequence select="xs:date('2013-01-16')"/>
to return an xs:date value and so on. Of course you can also return sequences with e.g. <xsl:sequence select="1, 2, 3"/>.
You wouldn't want to create a text node or even an element node in these cases in my view as it is inefficient.
So that is my take, with the new schema based type system of XSLT and XPath 2.0 a way is needed to return or pass around values of these types and a new construct was needed.
[edit]Michael Kay says in his "XSLT 2.0 and XPath 2.0 programmer's reference" about xsl:sequence: "This innocent looking instruction introduced in XSLT 2.0 has far reaching effects on the capability of the XSLT language, because it means that XSLT instructions and sequence constructors (and hence functions and templates) become capable of returning any value allowed by the XPath data model. Without it, XSLT instructions could only be used to create new nodes in a result tree, but with it, they can also return atomic values and references to existing nodes.".
Another use is to create a tag only if it has a child. An example is required :
<a>
<b>node b</b>
<c>node c</c>
</a>
Somewhere in your XSLT :
<xsl:variable name="foo">
<xsl:if select="b"><d>Got a "b" node</d></xsl:if>
<xsl:if select="c"><d>Got a "c" node</d></xsl:if>
</xsl:variable>
<xsl:if test="$foo/node()">
<wrapper><xsl:sequence select="$foo"/></wrapper>
</xsl:if>
You may see the demo here : http://xsltransform.net/eiZQaFz
It is way better than testing each tag like this :
<xsl:if test="a|b">...</xsl:if>
Because you would end up editing it in two places. Also the processing speed would depend on which tags are in your imput. If it is the last one from your test, the engine will test the presence of everyone before. As $foo/node() is an idioms for "is there a child element ?", the engine can optimize it. Doing so, you ease the life of everyone.
<xsl:variable name="date1" select="2011-10-05"/>
<xsl:variable name="date2" select="2011-10-05"/>
<xsl:variable name="date3" select="2011-10-06"/>
<xsl:if test="$date2 = $date1 or $date2 < $date1">
..do something
</xsl:if>
<xsl:if test="$date3 = $date1 or $date3 > $date1">
.. do something
</xsl:if>
Both should evaluate true, but the second if doesn't. For the life of me I can't comprehended why!
In the actual transform the dates themselves are being drawn from an XML document but debugging through VS2010 i can see the values are as above.
Must be something fairly fundamental i'm doing wrong - any help would be brilliant!
I tried this in Oxygen/XML... select="2011-10-05 is being interpreted as an arithmetic expression, giving the value 1996 (2011 minus 10 minus 5) and "2011-10-06" is intrepreted as 1995.
What you want is
<xsl:variable name="date1" select="'2011-10-05'"/>
<xsl:variable name="date2" select="'2011-10-05'"/>
<xsl:variable name="date3" select="'2011-10-06'"/>
Note the extra single quotes.
From the XSLT 1.0 Specification:
If the variable-binding element has a select attribute, then the value
of the attribute must be an expression and the value of the variable
is the object that results from evaluating the expression.
I'm receiving XML into BizTalk. One part is element and the value is ids separated by comma
<Stores>15,34</Stores>
I need to transform this into
<Stores>
<Store>Store 1</Store>
<Store>Store 4</Store>
</Stores>
What I need to do is to explode the value by comma, take each value and get value from database (15 -> Store 1, 34 -> Store 2).
How can I make the explode in xslt, how ca I get value from database for each exploded value. I already have procedure in db for that, just need to know how to call it.
Here is an XSLT 1.0 compatible solution that does the explode:
<!-- straightforward -->
<xsl:template match="Stores">
<xsl:copy>
<xsl:call-template name="explode">
<xsl:with-param name="str" select="text()" />
</xsl:call-template>
</xsl:copy>
</xsl:template>
<!-- string processing through recursion -->
<xsl:template name="explode">
<xsl:param name="str" select="''" />
<xsl:variable name="temp" select="concat($str, ',')" />
<xsl:variable name="head" select="substring-before($temp, ',')" />
<xsl:variable name="tail" select="substring-after($temp, ',')" />
<xsl:if test="$head != ''">
<Store>
<xsl:value-of select="$head" />
</Store>
<xsl:call-template name="explode">
<xsl:with-param name="str" select="$tail" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Output for <Stores>15,34</Stores> would be:
<Stores>
<Store>Store 15</Store>
<Store>Store 34</Store>
</Stores>
David Hall's solution seems to contain a pointer how to use an XSLT extension function to make calls to that database from XSLT.
The BizTalk Mapper does not support XSLT 2.0 (see MSDN Documentation http://msdn.microsoft.com/en-us/library/aa559261(BTS.10).aspx) so you will need to use the EXSLT extensions if you want to use the mapper.
There is a great post here by Richard Hallgren that covers how to use EXSLT within the BizTalk Mapper.
One additional thought is about an alternative solution. It is not clear if you absolutely must make your database calls one by one - would making a single call work?
It is possible to provide a stored procedure a delimited string as a parameter and to then use a function to break this string up. I've included an example of such a function below, the example being a table function. You'll be able find lots of other implementations on the web.
With the table function you can join against this in you store lookup procedure.
If this meets your needs it should be a lot faster since you now perform just a single database hit and can perform set operations to get back your list of stores.
CREATE function fn_ParseCSVString
(
#INPUTCSV varchar(MAX)
)
RETURNS #TBL TABLE
(
COL1 INT
)
AS
BEGIN
DECLARE #NUM_STR NVARCHAR(MAX)
SET #NUM_STR = #INPUTCSV
SET #NUM_STR = REPLACE(#NUM_STR,' ','')
-- this will trim any intermediate spaces
WHILE LEN(#NUM_STR) >= 0
BEGIN
DECLARE ##SUBSTR VARCHAR(100)
IF CHARINDEX(',',#NUM_STR,0) <> 0
BEGIN
SET ##SUBSTR = SUBSTRING(#NUM_STR,0,CHARINDEX(',',#NUM_STR,0))
INSERT INTO #TBL VALUES(##SUBSTR)
END
ELSE
BEGIN
INSERT INTO #TBL VALUES(#NUM_STR)
BREAK
END
SET ##SUBSTR = ##SUBSTR + ','
SET #NUM_STR = SUBSTRING(#NUM_STR, CHARINDEX(',',#NUM_STR,0) + 1, LEN(#NUM_STR))
END
RETURN
END
I assume you know how to write the overall transform but need help with the tokenization of the string containing the store numbers.
If you're using XSLT 2.0, look at the definition of the tokenize() function. This will split the string value at a specified delimiter, allowing you to perform this transformation. In XSLT 1 you could look at EXSLT regex extension functions.