xslt 1.0 substring-after to ignore case - xslt

I have 2 xml nodes like this, for example:
<Model>GRAND MODUS</Model>
<QualifiedDescription>2008 58 Reg Renault Grand Modus 1.2 TCE Dynamique 5drMetallic Flame Red</QualifiedDescription>
I'm trying to use substring-after to split the QualifiedDescription after the Grand Modus like this:
<xsl:variable name="something"><xsl:value-of select='substring-after(QualifiedDescription, Model)' /></xsl:variable>
But obviously it's not working being of it being case sensitive. Is it possible to get substring-after to work case insensitive, but still return the output with case preserved EG.
1.2 TCE Dynamique 5drMetallic Flame Red
Thanks.

You could convert the two strings to the same case using translate in order to work out the character offset of the first within the second, then take a substring of the original QualifiedDescription from that position.
<xsl:variable name="uc" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:variable name="lc" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="substrStart" select="
string-length(substring-before(translate(QualifiedDescription, $uc, $lc),
translate(Model, $uc, $lc)))
+ string-length(Model)
+ 1" /><!-- +1 because string indexes in XPath are 1-based -->
<xsl:variable name="something"
select="substring(QualifiedDescription, $substrStart)" />
You'd need slightly more complex logic to take account of cases where the QualifiedDescription does not include the Model (since in this case both substring-before and substring-after return the empty string) but you get the idea.

You can do case insensitive if you uppercase all first and substring on uppercase:
substring-after(upper-case(QualifiedDescription), upper-case(Model))

Related

xslt mapping to find substring between

I have my string as below
<Text>Pack:NA Lead:20 Dimension:235</Text>
And need to map
NA to outputfield1
20 to outputfield2
235 to outputfield3
How to do this correctly in xslt mapping where the values 'NA,20,235' could be different each time?
I could only see substring component which takes length as second parameter.
That leads requires several steps to achieve this.
Any better solution to just take the value between Lead: and Dimension for outputfield2?
To extract the Pack value, you can use:
<xsl:value-of select="substring-before(substring-after(Text, 'Pack:'), ' ')" />
To extract the Lead value, use:
<xsl:value-of select="substring-before(substring-after(Text, 'Lead:'), ' ')" />
To extract the Dimension:
<xsl:value-of select="substring-after(Text, 'Dimension:')" />

Get position of specific word

I am new in XSLT and if it is possible to get the position of a specific word? For example, I have a data like this:
<Data>The quick brown fox jumps over the lazy dog!</Data>
I want to get the position of a "brown", "over", "dog" and "!". And, store it in different output name. Like the position of brown is <foo>3</foo>, position of over is <boo>6</boo>, dog <hop>9</hop> and ! <po_df>10</po_df>. Is it possible?
If you were only looking for words you could use tokenize(., '\s+|\p{P}')
<xsl:template match="Data">
<xsl:copy>
<xsl:variable name="words" select="tokenize(., '\s+|\p{P}')"/>
<xsl:for-each select="'brown', 'over', 'dog'">
<matched item="{.}" at-pos="{index-of($words, .)}"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
which gives
<Data>
<matched item="brown" at-pos="3"/>
<matched item="over" at-pos="6"/>
<matched item="dog" at-pos="9"/>
</Data>
so it has the right positions (I am not sure where the names of the elements you posted (like hop) are to be taken from so I have not tried to implement that.).
As you also want to identify a punctuation character I am not sure tokenize suffices and even with analyze-string it is not straight-forward to match and collect the position. Maybe someone else has a better idea.

Space in number format for xsl

I have a number such as: 457342137 but i want to display it as a 457 342 137.
I have something like this:
<xsl:template match="klient/#numer_telefonu">
<xsl:variable name="numer" select="." />
<xsl:value-of select="format-number($numer, '### ### ###')" />
</xsl:template>
but it does not work.
If you want to use a non-standard 'grouping' separator, you first need define the symbols you are going to use in your format command as follows:
<xsl:decimal-format name="spaces" grouping-separator=" " />
Then, you can reference this format in the command itself as follows:
<xsl:value-of select="format-number($numer, '# ###', 'spaces')" />
Further information about decimal-format can be found at http://www.w3.org/TR/xslt#format-number
A telephone number is a string, not a number and you shouldn't try formatting it as one. Technically, you could do:
<xsl:value-of select="translate(format-number(., '#,###'), ',', ' ' )" />
to achieve the desired result in your example. However, given a "number" such as "057342137" the result will be "57 342 137" (leading zeros stripped). You should be using string functions to manipulate a string.

xslt choosing between select="1" or select="'1'"

What is the difference between <xsl:variable name="test" select="1"/>
and <xsl:variable name="test" select="'1'"/> ?
if both results are result tre fragments.. so basically the two lines of code above are identical?
If so. how do we decide which to use?
The first sample creates a variable of type number with the number value 1, the second a variable of type string with the string value "1". Result tree fragments are not created with your code samples, that would be done with <xsl:variable name="test">1</xsl:variable>.
As #Martin pointed out, the first one binds the variable to a number and the second one to a string.
how do we decide which to use?
Think of the use you will do with that variable. For example, in the first case you will be able to do:
item[$test]
This won't be possible in the second case, unless you use number() function.
As per the comments below, string or number will not make any difference when using any of the comparison operators. Even when comparing against nodes sets or rtfs. You can read this on the specs (a bit verbose) or try some silly test.
What still is evident is the different behavior you can obtain when dealing with node positions. For example, if you have:
<xsl:variable name="number2" select="2"/>
<xsl:variable name="string2" select="'2'"/>
<xsl:variable name="rtf2">2</xsl:variable>
and you have the input like this:
<root>
<test>a</test>
<test>b</test>
</root>
By using:
<xsl:value-of select="/root/test[$rtf2]"/>
<xsl:value-of select="/root/test[$string2]"/>
<xsl:value-of select="/root/test[$number2]"/>
You will get:
aab
while this:
<xsl:value-of select="/root/test[position()=$rtf2]"/>
<xsl:value-of select="/root/test[position()=$string2]"/>
<xsl:value-of select="/root/test[$number2]"/>
will return:
bbb
due to implicit conversion caused by comparison operators.
XPath 1.0 and XSLT 1.0 treat numbers and strings as pretty much interchangeable, with very few exceptions. A notable exception is item[$test]. But "=" behaves slightly differently too: as numbers 4 and 04 are equal, but as strings they are not.
In XPath 2.0 and XSLT 2.0 the type system is much richer and the difference between strings and numbers is much more noticeable: many operations defined on numbers won't work on strings, and vice-versa.
How to decide? If it's all-numeric, you would normally want to use a number, unless it's something like a phone number, where leading zeros are significant, and it's therefore not really a number but a string of digits.

Find value in sequence using XSL

I want to check if a value exists in a sequence defined as
<xsl:variable name="some_seq" select="/root/word[#optional='no']/text()"/>
In the past, I've had success with Priscilla Walmsleys function. For clarity, I reproduce it here as follows:
<xsl:function name="functx:is-value-in-sequence" as="xs:boolean">
<xsl:param name="value" as="xs:anyAtomicType?"/>
<xsl:param name="seq" as="xs:anyAtomicType*"/>
<xsl:sequence select="$value=$seq"/>
</xsl:function>
However, this time I need to make a case-insensitive comparison, and so I tried to wrap both $value and $seq with a lower-case(). Obviously, that didn't help much, as $seq is a sequence and lower-case() takes only strings.
Question: what is the best way to either 1) construct a sequence of lower-case strings, or 2) make a case-insensitive comparison analogous to $value=$seq above? TIA!
Question: what is the best way to
either 1) construct a sequence of
lower-case strings
Not many people realize that you can use a function as the last location step in an XPATH 2.0 expression.
You can create a sequence of lower-case() string values with this expression:
/root/word[#optional='no']/text()/lower-case(.)
or 2) make a case-insensitive
comparison analogous to $value=$seq
above?
Using that strategy, you can define a custom function that compares the lower-case() value of the $value and each string value in the $seq:
<xsl:function name="functx:is-value-in-sequence" as="xs:boolean">
<xsl:param name="value" as="xs:anyAtomicType?"/>
<xsl:param name="seq" as="xs:anyAtomicType*"/>
<xsl:sequence select="some $word in $seq/lower-case(.)
satisfies ($word = $value/lower-case(.))"/>
</xsl:function>
Use a "for-expression" inside the function to prepare a lower-case version of the sequence
<xsl:variable name="lcseq" select="for $i in $seq return lower-case($i)"/>
See Michael Kay's "XSLT 2.0 and XPATH 2.0, 4th ed", p. 640
(I haven't tested this)