I am pretty new to all of developing / using XSLT but for work we have to use this now.
I am busy mapping messages from application A to B and i run into something I cannot find the answer to.
as input field i have <sys_external_id>201000077_G001_S20_H10</sys_external_id>
now have i created below:
<stopNumber>
<xsl:value-of select="substring(sys_external_id, 16, 4)" />
</stopNumber>
<stopHandlingNumber>
<xsl:value-of select="substring(sys_external_id, 20, 4)" />
</stopHandlingNumber>
this gives me at the moment the correct answer ( S20 and H10 )
now is the "problem" that the value of S20 and H10 can differ.
for example the value can be S2100 or H110. also the G001 can also differ.
A substring-after does not work because i cannot determine where my end is ( as far as I know )
In XSLT 2.0 you could do:
<xsl:variable name="tokens" select="tokenize(sys_external_id, '_')" />
<stopNumber>
<xsl:value-of select="$tokens[3]" />
</stopNumber>
<stopHandlingNumber>
<xsl:value-of select="$tokens[4]" />
</stopHandlingNumber>
If you're limited to XSLT 1.0, then it could be:
<xsl:variable name="tail" select="substring-after(substring-after(sys_external_id, '_'), '_')" />
<stopNumber>
<xsl:value-of select="substring-before($tail, '_')" />
</stopNumber>
<stopHandlingNumber>
<xsl:value-of select="substring-after($tail, '_')" />
</stopHandlingNumber>
This is assuming that the input string is simply a sequence of tokens delimited by an underscore.
Note also that some XSLT 1.0 processors support the EXSLT str:tokenize() extension function.
Related
I'm doing an xslt transform that generates a sql statement for me. The way I'm using below is not working. Is there a way?
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select $var1.* from $var1
</xsl:template>
I know it will work if I do this:
<xsl:template match="foo">
select <xsl:value-of select="#att_val1" />.* from <xsl:value-of select="#att_val1" />
</xsl:template>
In XSLT 1.0, variable references are recognized in XPath expressions, but not in general template text. To evaluate an XPath expression and output the result as a text node in the result tree, use xsl:value-of, as you already know how to do. Example:
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select <xsl:value-of select="$var1"/>.* from <xsl:value-of select="$var1"/>
</xsl:template>
Alternatively, you could build the whole select command in one xsl:value-of with use of the concat() function.
Unless you move to XSLT 3.0 (https://www.w3.org/TR/xslt-30/#text-value-templates) where you can do e.g. <xsl:template match="foo" expand-text="yes">select {#att_val1}.* from {#att_val1}</xsl:template> you will have to use your second option or perhaps a <xsl:template match="foo"><xsl:value-of select="concat('select ', #att_val1, '.* from ', #att_val1)"/></xsl:template>, but there is certainly no way in XSLT 1.0 to avoid the use of xsl:value-of completely.
I am using Umbraco 4.5 (yes, I know I should upgrade to 7 now!)
I have an XSLT transform which builds up a list of products which match user filters.
I am making an XSL:variable which is a collection of products from the CMS database.
Each product has several Yes/No properties (radio buttons). Some of these haven't been populated however.
As a result, the following code breaks occasionally if the dataset includes products which don't have one of the options populated with an answer.
The error I get when it transforms the XSLT is "Value was either too large or too small for an Int32". I assume this is the value being passed into the GetPreValueAsString method.
How do I check to see if ./option1 is empty and if so, use a specific integer, otherwise use ./option1
<xsl:variable name="nodes"
select="umbraco.library:GetXmlNodeById(1098)/*
[#isDoc and string(umbracoNaviHide) != '1' and
($option1= '' or $option1=umbraco.library:GetPreValueAsString(./option1)) and
($option2= '' or $option2=umbraco.library:GetPreValueAsString(./option2)) and
($option3= '' or $option3=umbraco.library:GetPreValueAsString(./option3)) and
($option4= '' or $option4=umbraco.library:GetPreValueAsString(./option4))
]" />
Note: you tagged your question as XSLT 2.0, but Umbraco does not use XSLT 2.0, it is (presently) stuck with XSLT 1.0.
$option1= '' or $option1=umbraco.library:GetPreValueAsString(./option1)
There can be multiple causes for your error. A processor is not required to process the or-expression left-to-right or right-to-left, and it is even allowed to always evaluate both expressions, even if the first is true (this is comparable with bit-wise operators (unordered) in other languages, whereas boolean operators (ordered) in those languages typically use early breakout).
Another error can be that your option value in the context node is not empty and is not an integer or empty, in which case your code will always return an error.
You could expand your expression by testing ./optionX, but then you still have the problem of order of evaluation.
That said, how can you resolve it and prevent the error from arising? In XSLT 1.0, this is a bit clumsy (i.e., you cannot define functions and cannot use sequences), but here's one way to do it:
<xsl:variable name="pre-default-option">
<default>1</default>
<default>2</default>
<default>3</default>
<default>4</default>
</xsl:variable>
<xsl:variable name="default-option"
select="exslt:node-set($pre-default-option)" />
<xsl:variable name="pre-selected-option">
<option><xsl:value-of select="$option1" /></option>
<option><xsl:value-of select="$option2" /></option>
<option><xsl:value-of select="$option3" /></option>
<option><xsl:value-of select="$option4" /></option>
</xsl:variable>
<xsl:variable name="selected-option" select="exslt:node-set($pre-selected-option)" />
<xsl:variable name="pre-process-nodes">
<xsl:variable name="selection">
<xsl:apply-templates
select="umbraco.library:GetXmlNodeById(1098)/*"
mode="pre">
<xsl:with-param name="opt-no" select="1" />
</xsl:apply-templates>
</xsl:variable>
<!-- your original code uses 'and', so is only true if all
conditions are met, hence there must be four found nodes,
otherwise it is false (i.e., this node set will be empty) -->
<xsl:if test="count($selection) = 4">
<xsl:copy-of select="$selection" />
</xsl:if>
</xsl:variable>
<!-- your original variable, should now contain correct set, no errors -->
<xsl:variable name="nodes" select="exslt:node-set($pre-process-nodes)"/>
<xsl:template match="*[#isDoc and string(umbracoNaviHide) != '1']" mode="pre">
<xsl:param name="opt-no" />
<xsl:variable name="option"
select="$selected-option[. = string($opt-no)]" />
<!-- gets the child node 'option1', 'option2' etc -->
<xsl:variable
name="pre-ctx-option"
select="*[local-name() = concat('option', $opt-no)]" />
<xsl:variable name="ctx-option">
<xsl:choose>
<!-- empty option param always allowed -->
<xsl:when test="$option = ''">
<xsl:value-of select="$option"/>
</xsl:when>
<!-- if NaN or 0, this will return false -->
<xsl:when test="number($pre-ctx-option)">
<xsl:value-of select="$default-option[$opt-no]"/>
</xsl:when>
<!-- valid number (though you could add a range check as well) -->
<xsl:otherwise>
<xsl:value-of select="umbraco.library:GetPreValueAsString($pre-ctx-option)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- prevent eternal recursion -->
<xsl:if test="4 >= $opt-no">
<xsl:apply-templates select="self::*" mode="pre">
<xsl:with-param name="opt-no" select="$opt-no + 1" />
</xsl:apply-templates>
<!-- the predicate is now ctx-independent and just true/false
this copies nothing if the conditions are not met -->
<xsl:copy-of select="self::*[$option = $ctx-option]" />
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="pre" />
Note (1): I have written the above code by hand, tested only for syntax errors, I couldn't test it because you didn't provide an input document to test it against. If you find errors, by all means, edit my response so that it becomes correct.
Note (2): the above code generalizes working with the numbered parameters. By generalizing it, the code becomes a bit more complicated, but it becomes easier to maintain and to extend, and less error-prone for copy/paste errors.
In my ORACLE SOA BPEL process, we are receiving a value as string # 10.2345678956 but for target system we just need to send upto 4 places in decimal like 10.2345. Could you please tell me how we can achieve that using xslt.
also if source system send value as 10 even those in that case, to target system we need to send 10.0000(always 4 digit in decimal)
To truncate (i.e. round down) a number to 4 decimal digits precision, you can do:
floor($number * 10000) div 10000
To format the result to always display 4 decimal digits:
format-number(floor($number * 10000) div 10000, '0.0000')
This works in XSLT 1.0 and XSLT 2.0 alike.
If you don't care about the direction of rounding, then do simply:
format-number($number, '0.0000')
and be done with it.
You don't specify which version of XSLT you are using, but in XSLT 1.0 you should do this "by hand", like as follows:
Note: as Michael says in the comments, you can use format-number in XSLT 1.0. However, the chosen rounding is unclear. While this works, see Michael's answer for a more trivial solution.
<xsl:template match="/">
<xsl:variable name="input" select="10.23456789" />
<xsl:call-template name="truncate">
<xsl:with-param name="val" select="$input" />
</xsl:call-template>
</xsl:template>
<xsl:template name="truncate">
<xsl:param name="val" />
<xsl:variable name="adj">
<xsl:value-of select="$val"/>
<xsl:if test="not(contains($val, '.'))">.</xsl:if>
<xsl:text>0000</xsl:text>
</xsl:variable>
<xsl:variable name="before" select="substring-before($adj, '.')" />
<xsl:variable name="after" select="substring-after($adj, '.')" />
<xsl:value-of select="$before" />
<xsl:text>.</xsl:text>
<xsl:value-of select="substring($after, 1, 4)" />
</xsl:template>
And in XSLT 2.0 and 3.0, you can simply do as follows:
<xsl:value-of select="format-number($input, '#.0000')" />
But this will round up (halves to even), i.e. with your input it would give you 10.2346. If that is undesirable, you could use the trick suggested by fvu:
<xsl:value-of select="
format-number(xs:integer(number($input) * 10000) div 10000, '#.0000')" />
Both the XSLT 1.0 and XSLT 2.0/3.0 versions above will append the necessary zeroes up to four.
I'm trying to substitute an empty value in a csv file with a number.
Here's an example:
1111111,,11222
So I tried this:
<xsl:template match="/">
<xsl:apply-templates select="//tr" />
</xsl:template>
<xsl:template match="tr">
<document>
<content name="title">
<xsl:value-of select="td[1]/text()" />
</content>
<content name="loanID">
<xsl:value-of select="td[1]/text()" />
</content>
<content name="cNumber">
<xsl:variable name="score" select="td[2]/text()" />
<xsl:choose>
<xsl:when test="$score=''">
<xsl:value-of select="550" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="td[18]/text()" />
</xsl:otherwise>
</xsl:choose>
</content>
</document>
</xsl:template>
I constantly get a null value for the cNumber node when the value is empty in the row, and I'm expecting my code to substitute the empty value for '550'. What am I doing wrong? I checked this question here: and it seems like this should work. I'm using a special application for this but my guess is the fault lies with me.
Thanks
If the td element is empty, then td/text() returns an empty sequence/node-set, and when you compare an empty sequence to '', the result is false. This is one of the reasons that many people advise against using text(). You're not interested here in the text node, you are interested in the string value of the td element, and to get that you should use
<xsl:variable name="score" select="string(td[2])" />
Your other uses of text() are also incorrect, though you're only likely to see a problem if your XML input contains comments or processing instructions. But you should get out of this coding habit, and replace
<xsl:value-of select="td[1]/text()" />
by
<xsl:value-of select="td[1]" />
As a general rule, when you see /text() in an XPath expression it's usually wrong.
I have a parameter in my XSLT which usually is a proper set of nodes that I apply templates on.
<apply-templates select="$conferences" />
However, sometimes something goes wrong and it comes in as a string. In this case I just want to skip applying templates. But what is the proper way of checking this? I could check that it's not a string of course, but how can I check that the parameter is... "templatable"?
<if test=" ? ">
<apply-templates select="$conferences" />
</if>
Since you're in XSLT 2.0 you can simply do
<xsl:if test="$conferences instance of node()*">
You can do:
<apply-templates select="$conferences/*" />
Which will only apply if there is an XML in it. Strings will not be applied.
If you want to do a condition up front, do something like:
<xsl:choose>
<xsl:when test="count($conferences/*) > 0"> <!-- it is XML -->
<xsl:apply-templates select="$conferences/*" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$conferences" /> <!-- it is not XML -->
</xsl:otherwise>
</xsl:choose>