XSLT Date comparison for When loop - xslt

I am comparing dates in xsl:stylesheet version="1.0" and if date difference is greater than 30 days than update Pkg variable by 0. But it seems its not working.
<xsl:variable name="Packagedate" select="//segment[#name='Date']/value"></xsl:variable>
<xsl:variable name="date" select="substring($Packagedate, 5, 2)"/>-<xsl:value-of select="substring($Packagedate, 7)"/>-<xsl:value-of select="substring($Packagedate, 1, 4)"></xsl:variable>
<xsl:variable name="CurrentDate" select="format-dateTime(current-dateTime(),'[Y0001]-[M01]-[D01]')"></xsl:variable>
<xsl:when test="days-from-duration(xs:date($CurrentDate)- xs:date('$date)) > 30">
<div class="fieldvalue">
<xsl:variable name="Pkg"></xsl:variable>
<xsl:value-of select='0'/>
</div>
</xsl:when>

You are using functions such as days-from-duration() and xs:date() that are only available in XSLT 2.0+, but you are running your stylesheet under an XSLT 1.0 processor. Options that might be available to you include:
Upgrading to a different XSLT processor
Using the add-on date/time library at exslt.org
Calling out to functions in your host programming language (e.g. Java or C#).

Related

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).

Using an xsl param as argument to XPath function

I've been trying to figure out a way to use a param/variable as an argument to a function.
At the very least, I'd like to be able to use basic string parameters as arguments as follows:
<xsl:param name="stringValue" default="'abcdef'"/>
<xsl:value-of select="substring(string($stringValue),1,3)"/>
The above code generates no output.
I feel like I'm missing a simple way of doing this. I'm happy to use exslt or some other extension if an xslt 1.0 processor does not allow this.
Edit:
I am using XSL 1.0 and transforming using Nokogiri, which supports XPATH 1.0 . Here is a more complete snippet of what I am trying to do:
I want to pass column numbers as parameters using nokogiri as follows
document = Nokogiri::XML(File.read('table.xml'))
template = Nokogiri::XSLT(File.read('extractTableData.xsl'))
transformed_document = template.transform(document,
["tableName","'Problems'", #Table Heading
"tablePath","'Table'", #Takes an absolute XPATH String
"nameColumnIndex","2", #column number
"valueColumnIndex","3"]) #column number
File.open('FormattedOutput.xml', 'w').write(transformed_document)
My xsl then wants to access every TD[valueColumnIndex] and and retrieve the first 3 characters at that position, which is why I am using a substring function. So I want to do something like:
<xsl:value-of select="substring(string(TD[$valueColumnIndex]),1,3)"/>
Since I was unable to do that, I tried to extract TD[$valueColumnIndex] to another param valueCode and then do substring(string(valueCode),1,3)
That did not work either (which is to say, no text was output, whereas <xsl:value-of select="$valueCode"/> gave me the expected output).
As a result, i decided to understand how to use parameters better, I would just use a hard coded string, as mentioned in my earlier question.
Things I have tried:
using single quotes around abcdef (and not) while
using string() around the param name (and not)
Based on the comments below, it seems I am handicapped in my ability to understand the error because Nokogiri does not report an error for these situations. I am in the process of installing xsltproc right now and seeing if I receive any errors.
Finally, here is my entire xsl. I use a separate template forLoop because of the valueCode param I am creating. The lines of interest are the last 5 or so. I cannot include the xml as there are data use issues involved.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:dyn="http://exslt.org/dynamic"
exclude-result-prefixes="ext dyn">
<xsl:param name="tableName" />
<xsl:param name="tablePath" />
<xsl:param name= "nameColumnIndex" />
<xsl:param name= "valueColumnIndex"/>
<xsl:template match="/">
<xsl:param name="tableRowPath">
<xsl:value-of select="$tablePath"/><xsl:text>/TR</xsl:text>
</xsl:param>
<!-- Problems -->
<section>
<name>
<xsl:value-of select="$tableName" />
</name>
<!-- <xsl:for-each select="concat($tablePath,'/TR')"> -->
<xsl:for-each select="dyn:evaluate($tableRowPath)">
<!-- Encode record section -->
<xsl:call-template name="forLoop"/>
</xsl:for-each>
</section>
</xsl:template>
<xsl:template name="forLoop">
<xsl:param name="valueCode">
<xsl:value-of select="./TD[number($valueColumnIndex)][text()]"/>
</xsl:param>
<xsl:param name="RandomString" select="'Try123'"/>
<section>
<name>
<xsl:value-of select="./TD[number($nameColumnIndex)]"/>
</name>
<code>
<short>
<xsl:value-of select="substring(string($valueCode),1,3)"/>
</short>
<long>
<xsl:value-of select="$valueCode"/>
</long>
</code>
</section>
</xsl:template>
</xsl:stylesheet>
Use it this way:
<xsl:param name="stringValue" select="'abcdef'"/>
<xsl:value-of select="substring($stringValue,1,3)"/>

schematron localfunctions gives error

I have schematron file which contains xsl function.
I get this error : "the function functionName was not found in namespace localFunctions"
Here is my schematron codes:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron"
xmlns:sch="http://purl.oclc.org/dsdl/schematron"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fct="localFunctions"
queryBinding="xslt2" >
<ns prefix="gl-plt" uri="http://www.xbrl.org/int/gl/plt/2010-04-16" />
<ns prefix="gl-cor" uri="http://www.xbrl.org/int/gl/cor/2006-10-25" />
<ns prefix="gl-bus" uri="http://www.xbrl.org/int/gl/bus/2006-10-25" />
<ns prefix="xbrli" uri="http://www.xbrl.org/2003/instance" />
<ns prefix="edefter" uri="http://www.edefter.gov.tr" />
<ns prefix="fct" uri="localFunctions" />
<title></title>
<!-- <gl-cor:accountingEntries> elemanı bir <gl-cor:entityInformation> elemanı içermelidir. -->
<pattern id="accountingentries">
<rule context="/edefter:defter/xbrli:xbrl/gl-cor:accountingEntries">
<let name="accoundMainIdList" value="gl-cor:entryHeader/gl-cor:entryDetail[1]/gl-cor:account/normalize-space(gl-cor:accountMainID)"/>
<assert test="fct:isSorted($accoundMainIdList)">Büyük defterde hesaplar, ana hesap numarası bazında sıralı olmalıdır.</assert>
</rule>
</pattern>
<xsl:function name="fct:isSorted" as="xs:boolean">
<xsl:param name="accoundMainIdList" as="xs:string*"/>
<xsl:variable name="sortedAccountMainIdList" as="xs:string*">
<xsl:for-each select="$accoundMainIdList">
<xsl:sort/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="s1">
<xsl:value-of select="string-join($accoundMainIdList,'|')"/>
</xsl:variable>
<xsl:variable name="s2">
<xsl:value-of select="string-join($sortedAccountMainIdList,'|')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$s1 = $s2">
<xsl:value-of select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</schema>
Why isSorted xsl function not found in namespace ?
I'm assuming that you are aware that the conventional method of running Schematron schemas is to process them using XSLT to generate an XSLT stylesheet which can be executed against your XML instance.
Looking on github, the XRouters Schematron projects is using a very old version of the standard XSLT stylesheets from http://www.schematron.com/. These are XSLT 1.0 stylesheets and are not capable of generating XSLT 2.0 stylseheets.
The xsl:function element you are trying to use is part of XSLT 2.0. Given that your tool is generating XSLT 1.0 it seems very unlikely that your XSLT 2.0 function will operate.
If you want to try an approach that may be more successful, may I suggest that you obtain a copy of Saxon HE (the .net version) from http://saxon.sf.net/. You can then build a simple set of XSLT transforms that will give you more of a chance of getting what you want.
I think #Nic's answer is in the right direction, but missed the mark.
Although the standard way of running schematron schemas against XML documents is to convert them to XSLT, that approach has a number of drawbacks (speed and having validation report you can process programmatically are two primary ones).
The XRouter SchemaTron is a native .NET implementation of ISO schematron. It doesn't convert the schematron schemas to XSLT stylesheets -- it parses them in memory and then processes each pattern/rule/assert to detect violations.
The problem is certainly rooted in a lack of XSLT2 support, but is not related at all to XSLT schematron stylesheets (they arent used, and are included in the project only as reference). Schematron specification doesn't require XSLT2 (or XPATH2) -- it leaves the query language to the schematron rules file. The branch you were using was probably the one delivering XPATH2 support via "Lightweight XPath2 for .NET", which doesn't support XSLT2.
I recommend using the branch of the project integrated with XmlPrime, with supports XSLT2. We're using that version with great success.

Setting an XSL variable to a node-set using conditions

This question is very similar to XSL store node-set in variable. The major difference is that if XPath does not find a node which matches the filter, I would like to return the first unfiltered result.
The code I have here works, but I feel like it is a hack and not good XSL style. In this case, each chapter node is identified by a string id. The variable showChapter is the string identifying a chapter. If no chapter is found with this id attribute, I want to return the first chapter.
Relevant code:
<xsl:param name="showChapter" />
<!-- if $showChapter does not match any chapter id attribute,
set validShowChapter to id of first chapter.
-->
<xsl:variable name="validShowChapter">
<xsl:choose>
<xsl:when test="/book/chapter[#id=string($showChapter)][position()=1]">
<xsl:value-of select="$showChapter" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/book/chapter[position()=1]/#id" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- I want $chapter to be a valid node-set so I can use it in
XPath select statements in my templates
-->
<xsl:variable
name="chapter"
select="/book/chapter[#id=string($validShowChapter)][position()=1]"
>
Is this approach as poor of a hack as I think it is, and if so could you point me to a better solution? I am using XSLT 1.0 processed by PHP5's XSLTProcessor, but XSLT 2.0 solutions are welcome.
The following should work. A lot of the usage of position() and string() in your example were unneeded, btw:
<xsl:param name="showChapter" />
<xsl:variable name="foundChapter" select="/book/chapter[#id = $showChapter]" />
<!-- Will select either the first chapter in $foundChapter, or
the first chapter available if $foundChapter is empty -->
<xsl:variable name="chapter"
select="($foundChapter | /book/chapter[not($foundChapter)])[1]" />

How do I know how many tags an xsl:variable contains?

In XSLT 1.0, if I have an <xsl:variable> declared like that:
<xsl:variable name="ListeEcheances">
<bla/><bli/>
</xsl:variable>
How do I know if it's empty? Or even better: how do I know how many tags it contains? (I know there are 2 tags here, but my real code is a little bit more complex :))
<xsl:when test="$ListeEcheances=''"> returns true (it doesn't count the tags, only the text) ;
<xsl:when test="count($ListeEcheances/*) > 0"> sadly doesn't compile.
Thank you for your help.
This is indeed incorrect and your compiler is correct in throwing an error. You can only count a node set, you cannot count a result tree fragment. What you need is transform the variable in a node-set by using an extension function.
For Saxon 6.5 this would be exsl:node-set.This works with Saxon 6.5 and any processor that supports the EXSLT node-set function (most do). EDIT: Jirka Kosek wrote down a list of node-set extensions per processor, I'm sure yours is in the list.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:exsl="http://exslt.org/common">
<xsl:variable name="ListeEcheances">
<bla/><bli/>
</xsl:variable>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="count(exsl:node-set($ListeEcheances)/*) > 0">
<xsl:text>Larger then zero!</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>Larger then zero!
Note: if you were to use XSLT 2.0, everything is a node-set and you don't run into this awkwardness of XSLT 1.0, where result tree fragments are next to useless.
If the content of the variable is declared in the XSLT as shown in your example, rather than dynamically evalueated, you can use the document() function to parse the XSLT file(which is an XML file) and evaluate an XPath expression to count the elements in the variable:
count(document('')/*/xsl:variable[#name='ListeEcheances']/*)
Using the document function with an empty path will load the base URI of the current stylesheet.
Try <xsl:when test="count($ListeEcheances/*) > 0">
or wait - maybe you get something like
Expression must evaluate to a node-set.
count(-->$ListeEcheances<--/*) > 0
The reason is that the variable is a result tree fragment, not a node-set.
In XSLT 1.0 you will need to apply the node-set function, available in a namespace dependent on the processor.
For instance: <xsl:when test="count(msxsl:node-set($ListeEcheances/*)) > 0">
If that does not work, or if you can't discover the namespace to use, then a trick might help:
<xsl:variable name="temp" select="$ListeEcheances"/>
<xsl:when test="count($temp/*) > 0">
The reason that this works can be found in stackoverflow rtf to node-set