Xpath test if is numeric OR valid numeric expression - xslt

It is quite simple to find out if a node value is numeric by for example:
<xsl:if test="string(number(myNode)) != 'NaN'">
<!-- myNode is numeric -->
</xsl:if>
But, what if myNode contains a fraction or other valid numeric expression? What is the best way to find that out?
Example XML:
<data id="1">34</data>
<data id="2">52.3</data>
<data id="3">5/9</data>
<data id="4">10*20</data>
<data id="5">foo</data>
Example XSL:
<xsl:for-each select="//data">
<xsl:if test="string(number(.)) != 'NaN'">
<isNumeric><xsl:value-of select="#id"/></isNumeric>
</xsl:if>
</xsl:if>
Will generate the output:
<isNumeric>1</isNumeric>
<isNumeric>2</isNumeric>
But what is the easiest way to write the xpath expression (or xsl code) to generate the following output:
<isNumeric>1</isNumeric>
<isNumeric>2</isNumeric>
<isNumeric>3</isNumeric>
<isNumeric>4</isNumeric>
...since data id 3 and 4 also are valid numeric expressions.
It is ok to assume the node value does not contain any spaces as this is controlled by the schema used.

Assuming you don't need to validate your expressions so 4/0 or even 4/+/9 are OK then
<xsl:if test="translate(.,'0123456789.+-/* ','')=''">
<isNumeric><xsl:value-of select="#id"/></isNumeric>
</xsl:if>

Related

ibm-datapower gives "&gt" in place of ">" and "&lt" in place of "<" in resulting XML for text data using XSLT

In one of my application, I am trying to convert the response of my service with the help of xslt on datapower.
In one of the response scenario, I need to show an xml something like below:
<data contentType="text/xml;charset=utf-8" contentLength="80"><![CDATA[Your request cannot be processed]]></data>
But my XSLT fails on datapower and it shows ">" and "<" in place of ">" and "<".
Below are my some of the attempted templates. Kindly have a look and suggest any correction:
Attempt 1:Tried with ">" and "<"
<xsl:param name="mask" select="'Your request cannot be processed'"/>
<xsl:template match="*" mode="copyFault">
<xsl:text disable-output-escaping="yes"><data contentType="text/xml;charset=utf-8" contentLength="80"><![CDATA[</xsl:text>
<xsl:value-of select="$mask" />
<xsl:text disable-output-escaping="yes">]]></data></xsl:text>
</xsl:template>
Attempt 2:Tried with HEX values
<xsl:param name="mask" select="'Your request cannot be processed'"/>
<xsl:variable name="lessThan" select="'<'"/>
<xsl:variable name="GreaterThan" select="'>'"/>
<xsl:template match="*" mode="copyFault">
<xsl:value-of disable-output-escaping = "yes" select="$lessThan"/>
<xsl:text>data contentType="text/xml;charset=utf-8" contentLength="80"</xsl:text>
<xsl:value-of disable-output-escaping = "yes" select="$GreaterThan"/>
<xsl:value-of disable-output-escaping = "yes" select="$lessThan"/>
<xsl:text>![CDATA[</xsl:text>
<xsl:value-of select="$mask" />
<xsl:text>]]</xsl:text>
<xsl:value-of disable-output-escaping = "yes" select="$GreaterThan"/>
<xsl:value-of disable-output-escaping = "yes" select="$lessThan"/>
<xsl:text>/data</xsl:text>
<xsl:value-of disable-output-escaping = "yes" select="$GreaterThan"/>
</xsl:template>
Please let me know what should I do to get the xml in proper format from datapower.
Thanks.
The usual way in XSLT to output a certain XML element is a literal result element so using
<data contentType="text/xml;charset=utf-8" contentLength="80">Your request cannot be processed</data>
in your XSLT will then output that element in the result. If you want to populate the element with a variable or parameter value then use e.g.
<data contentType="text/xml;charset=utf-8" contentLength="80"><xsl:value-of select="$mask"/></data>
If the XSLT processor is in charge of serializing the result to a file or string and you want some element like the data element to have a CDATA section as the content then declare e.g. <xsl:output cdata-section-elements="data"/> as a child of xsl:stylesheet (or xsl:transform if you have named the root element that way).
disable-output-escaping is a thoroughly nasty feature: it doesn't work on all processors, and if it's supported at all, it only works when the transformation output is fed directly into an XSLT-aware serializer, so it depends on how you are running the transformation.
It's much better to avoid disable-output-escaping when you can, and there's certainly no evidence you need it here. The requirement to output a CDATA section is somewhat unusual (any well-written application reading XML doesn't care whether the text is in a CDATA section or not), but if you really need it, then you can usually achieve it using <xsl:output cdata-section-elements="data"/>. (Though again, this only works if the output is fed into an XSLT-aware serializer.)
Certainly, generating start and end tags using disable-output-escaping is very poor practice.

How to find smallest string in result tree fragment(RTF) containing two strings without using extension function e.g. node-set in xslt 1.0

I have two strings variables basically storing currency codes which can have values like USD or EUR or JPY etc.
Examples:
If variable boughtccy contains 'JPY' and variable soldccy contains 'USD' then it should return a string 'BOUGHTCCY' signifying that it is the variable named boughtccy actually contains the smallest currency.
Similarly, if variable boughtccy contains 'EUR' and variable soldccy contains 'AUD' then it should return a string 'SOLDCCY' signifying that it is the variable named soldccy actually contains the smallest currency.
I have written following code which works perfectly fine except for in Altova XMLSpy. I have a requirement that it should also work in XMLSpy.
Here is the code:
xsl:variable name="smallerccy">
<xsl:variable name="nodes">
<node>
<xsl:value-of select="$boughtccy"/>
</node>
<node>
<xsl:value-of select="$soldccy"/>
</node>
</xsl:variable>
<xsl:for-each select="common:node-set($nodes)/*">
<xsl:sort select="."/>
<xsl:choose>
<xsl:when test="position()=1 and .=$boughtccy">BOUGHTCCY</xsl:when>
<xsl:when test="position()=1 and .=$soldccy">SOLDCCY</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
How do I achieve this without using exslt:node-set() function? I cannot use xslt 2.0.
I believe the underlying problem you are having is that in XSLT 1.0 strings can only compared only for equality (or inequality), and so you can simply do <xsl:when test="$boughtccy > $soldccy">
In you particular case, where you are dealing with a finite number of strings, you could define a variable containing all possible currency codes, and use string handling to find one is first.
Try this XSLT (You would, of course, have to amend $AllCurrencies to have all possible currency codes).
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="AllCurrencies" select="'AUS|EUR|JPY|USD'" />
<xsl:param name="boughtccy" select="'JPY'" />
<xsl:param name="soldccy" select="'USD'" />
<xsl:template match="/">
<xsl:choose>
<xsl:when test="string-length(substring-before($AllCurrencies, $boughtccy)) < string-length(substring-before($AllCurrencies, $soldccy))">BOUGHTCCY</xsl:when>
<xsl:otherwise>SOLDCCY</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I would write conditional logic, using system-property() and function-available(), that implements this in different ways for different processors:
if it's XSLT 2.0, use min(($boughtccy, $soldccy))
if it's XSLT 1.0 with a node-set extension, use the sorting technique that you're already using
otherwise give up. (There is no way to compare two strings for "<" in XSLT 1.0 other than creating a node-set and sorting it, and that requires the node-set extension).

XSLT and XPATH to find the correct element using data from sibling elements

I'm having trouble locating the correct syntax for finding the value of an element based on sibling element data.
Given that I have the following XML:
<files>
<file>
<location>location1.txt</location>
<metadata>
<foo>some value 1</foo>
</metadata>
</file>
<file>
<location>location2.txt</location>
<metadata>
<foo>some value 2</foo>
</metadata>
</file>
<file>
<location>location3.txt</location>
<metadata />
</file>
</files>
What is the correct XPATH expression to find the correct location value if I'm looking for only the location where file/metadata/foo = "some value 2"? I can't seem to find the right syntax to locate it if I'm dependent on data in a sibling, or the children of that sibling.
I've found a solution where I use xslt to iterate through the file elements, and find the appropriate value that way, but it's not very elegant.
<xsl:variable name="profileAlias">
<xsl:for-each select="files/file">
<xsl:if test="metadata/foo='some value 2'">
<xsl:value-of select="location">
</xsl:if>
</xsl:for-each>
</xsl:variable>
If you need to take one only value you can try this one, if this is elegent for you :D
<xsl:value-of select="files/file/location[following-sibling::metadata/foo/text()='some value 2']"/>
files/file/location - takes location attribute
[following-sibling::metadata/foo/ - which has next attribute metadata/foo/
text()='some value 2'] - with text equal 'some value 2'
Try
/files/file[metadata/foo='some value 2']/location

xpath for selecting elements having a child value specified in a list

Consider the following case:
<xsl:variable name="list">
<row>
<webid>2</webid>
</row>
<row>
<webid>3</webid>
</row>
<row>
<webid>4</webid>
</row>
</xsl:variable>
<!--pseudo code, not sure if it will work in real time-->
<xsl:variable name="addr" select="addresses/item[not(id=$list/webid)]"/>
I want to filter those addresses items which have an id not in the $list webid collection. Will the expression I posted do? Please advice.
In XSLT 1.0 a variable declaration like that is a result tree fragment, not a node set, and the only thing you can do with it is copy-of or value-of - you can't navigate into it with XPath expressions. You need to either
Use an extension function such as exslt:node-set to turn the RTF into a real node set or
Use a trick with document('') to access the style sheet itself as an XML document
Option 2 only works in cases like your example, where the variable value is static XML. If you have a variable whose value is built using xsl: commands or attribute value templates, e.g.
<xsl:variable name="allNames">
<xsl:for-each select="name">
<person name="{.}" />
</xsl:for-each>
</xsl:variable>
then you'd have to use a node-set function. The document('') approach would give you the actual xsl:for-each element and a literal value of {.} for the name attribute, rather than the result of evaluating it.
Option 1
Add xmlns:exslt="http://exslt.org/common" exclude-result-prefixes="exslt" to your <xsl:stylesheet> tag, then use
<xsl:variable name="addr" select="addresses/item[
not(id=exslt:node-set($list)/row/webid)]"/>
Option 2
<xsl:variable name="addr" select="addresses/item[
not(id=document('')//xsl:variable[name='list']/row/webid)]"/>
I put not(addresses/item[id=$list/webid])

In XSLT for MODS XML to FilemakerPro conversion, how treat a mod with a parameter?

Referring to query XSLT to translate Zotero xml output to FMPXMLRESULT xml?, how in the XSLT do I refer to a mod such as:
<genre authority="marcgt"> book </genre>
(There are multiple mods beginning genre authority, each with a different parameter. So what is the syntax in the XSLT for that? I tried the following, but Filemaker says there's a syntax error.
<COL>
<DATA>
<xsl:value-of select="mod:genre authority=""marcgt""" />
</DATA>
</COL>
The select is looking for a node to pick, it seems like you are trying to point it to:
<xsl:value-of select="mod:genre[#authority='marcgt']" />