XSL conversion from 2.0 - xslt

I have a problem converting XSLT 2.0 to XSLT 3.0. I want to use the streaming capability of XSLT 3.0. I am struggling with using the <xsl:stream> and <xsl:mode> tags as XSLT 3.0 has several restrictions. For using XSLT 3.0 streaming capabilities, following restrictions are faced:
“Sibling nodes and ancestor sibling are not reachable”.
"You can visit child nodes only once"
"You have access only to the current element attributes and namespace declaration"
How can I overcome these restrictions? Can anyone please help me?
<?xml version="1.0" encoding="UTF-8">
<xsl:stream href="SampleInput3_0.xml">
<xsl:for-each select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID)">
<Patient>
<ucfd:Name>
<xsl:variable name="varFirst_Name" as="node()" select="ns0:PID.5/ns0:XPN.2[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]" />
<xsl:variable name="varLast_Name" as="node()" select="ns0:XPN.1/ns0:FN.1[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]" />
<xsl:attribute name="value" namespace="" select="concat(string($varFirst_Name), string(varLast_Name))" />
</ucfd:Name>
<ucfd:FirstName value="{ns0:PID.5/ns0:XPN.2[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:LastName value="{ns0:PID.5/ns0:XPN.1/ns0:FN.1}" />
<ucfd:MiddleName value="{ns0:PID.5/ns0:XPN.3[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Prefix value="{ns0:PID.5/ns0:XPN.5[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Suffix value="{ns0:PID.5/ns0:XPN.4[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Identification>
<ucfd:TaxIdentifier>
<ucfd:Type value="{ns0:PID.18/ns0:CX.5[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Identifier value="{ns0:PID.18/ns0:CX.1}" />
</ucfd:TaxIdentifier>
</ucfd:Identification>
<xsl:for-each select="ns0:PID.11">
<ucfd:Address>
<xsl:if test="exists(#Type)">
<ucfd:Type value="{ns0:XAD.7[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
</xsl:if>
<ucfd:City value="{ns0:XAD.3[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:State value="{ns0:XAD.4[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:PostalCode value="{ns0:XAD.5[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Country value="{ns0:XAD.6[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:County value="{ns0:XAD.9[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
</ucfd:Address>
</xsl:for-each>
<ucfd:Contact>
<ucfd:Role value="{ns0:PID.13/ns0:XTN.2[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Type value="{ns0:PID.13/ns0:XTN.3[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<xsl:for-each select="ns0:PID.13">
<!--<xsl:variable name="var9_current" as="node()" select="."/>-->
<ucfd:CommunicationNumber>
<xsl:if test="exists(#Type)">
<ucfd:Type>
<xsl:sequence select="()" />
</ucfd:Type>
</xsl:if>
<ucfd:Identifier>
<!--<xsl:variable name="var8_current" as="node()" select="ns0:XTN.5[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]"/>
<xsl:variable name="var7_current" as="node()" select="ns0:XTN.6[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]"/>
<xsl:variable name="var6_current" as="node()" select="ns0:XTN.7[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]"/>
<xsl:variable name="var5_current" as="node()" select="ns0:XTN.8[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]"/>
<xsl:variable name="var4_current" as="node()" select="ns0:XTN.10[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]"/>
<xsl:variable name="var3_current" as="node()" select="ns0:XTN.11[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]"/>
<xsl:attribute name="value" namespace="" select="concat(concat(concat(concat(concat(string($var8_current), string($var7_current)), string($var6_current)), string($var5_current)), string($var4_current)), string($var3_current))"/> -->
</ucfd:Identifier>
</ucfd:CommunicationNumber>
</xsl:for-each>
</ucfd:Contact>
<ucfd:Demographics>
<ucfd:BirthDate>
<xsl:sequence select="()" />
</ucfd:BirthDate>
<ucfd:BirthSequenceNumber>
<xsl:for-each select="ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID/ns0:PID.25">
<xsl:attribute name="value" namespace="">
<xsl:if test="not((translate(string(#xsi:nil), 'true ', '1') = '1'))">
<xsl:sequence select="xs:string(xs:integer(string(.)))" />
</xsl:if>
</xsl:attribute>
</xsl:for-each>
</ucfd:BirthSequenceNumber>
<ucfd:DeathDate>
<xsl:sequence select="()" />
</ucfd:DeathDate>
<ucfd:Gender value="ns0:PID.8[not((translate(string(#xsi:nil), 'true ', '1') = '1'))]" />
<ucfd:MaritalStatus value="{ns0:PID.16/ns0:CE_0002.1[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:CitizenshipStatus value="{ns0:PID.26[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:RaceOrEthnicity>
<ucfd:Race value="{ns0:PID.10/ns0:CE_0005.1[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
</ucfd:RaceOrEthnicity>
<ucfd:Religion value="{ns0:PID.17/ns0:CE_0006.2[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
</ucfd:Demographics>
<ucfd:Language>
<ucfd:LanguageCode value="{ns0:PID.15/ns0:CE_0296.1[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:Description value="{ns0:PID.15/ns0:CE_0296.2[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
<ucfd:UseIndicator value="{ns0:PID.15/ns0:CE_0296.3[not(translate(#xsi:nil, 'true ', '1') = '1')]}" />
</ucfd:Language>
</Patient>
</xsl:for-each>
</xsl:stream>
</MemberRecord>
</xsl:template>
</xsl:stylesheet>

I think you should follow the approach in http://saxonica.com/documentation/index.html#!sourcedocs/streaming/burst-mode-streaming so along the lines of
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="..."
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="..." xmlns:ccfd="..." xmlns:hcfd="..." xmlns:ucf="..." xmlns:ucfd="..."
exclude-result-prefixes="ns0 xs">
<xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>
<!-- start with this named template -->
<xsl:template name="main">
<MemberRecord xsi:schemaLocation="...">
<Patient>
<xsl:stream href='employees.xml'>
<xsl:apply-templates select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID)"/>
</xsl:stream>
</Patient>
</MemberRecord>
</xsl:template>
<xsl:template match="ns0:PID">
<ucfd:Name value="{...}"/>
<ucfd:FirstName value="{ns0:PID.5/ns0:XPN.2[not(translate(#xsi:nil, 'true ', '1') = '1'))]}"/>
<ucfd:LastName value="{ns0:PID.5/ns0:XPN.1/ns0:FN.1}"/>
<!-- compute the other elements and attributes here as above -->
</xsl:template>
</xsl:stylesheet>
That should show the approach, you will have to add code for the remaining elements like middle name and prefix and so on. I have also tried to simplify the compuation of the attribute value in some cases, I think the same can be done for the other cases but I think you should ask that in a different question where you provide details of the XML structure and the values you want to compute, all those nested for-eachs to generate a single value attribute seem a bit convoluted.
Also note that I have followed your posted code in creating a single Patient element, I would however expected that a ns0:ORM_O01.PATIENT element is mapped to a Patient so more along the lines of
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="..."
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="..." xmlns:ccfd="..." xmlns:hcfd="..." xmlns:ucf="..." xmlns:ucfd="..."
exclude-result-prefixes="ns0 xs">
<xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>
<!-- start with this named template -->
<xsl:template name="main">
<MemberRecord xsi:schemaLocation="...">
<xsl:stream href='employees.xml'>
<xsl:apply-templates select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT)"/>
</xsl:stream>
</Patient>
</MemberRecord>
</xsl:template>
<xsl:template match="ns0:ORM_O01.PATIENT">
<Patient>
<xsl:apply-templates/>
</Patient>
</xsl:template>
<xsl:template match="ns0:PID">
<ucfd:Name value="{...}"/>
<ucfd:FirstName value="{ns0:PID.5/ns0:XPN.2[not(translate(#xsi:nil, 'true ', '1') = '1'))]}"/>
<ucfd:LastName value="{ns0:PID.5/ns0:XPN.1/ns0:FN.1}"/>
<!-- compute the other elements and attributes here as above -->
</xsl:template>
</xsl:stylesheet>

Related

Increment the repeated value in xslt inside for-each

Here I want to count/Increment the repeated emp_no using for each.
and I just want to concatenate emp_no with duplicate no count..
Here is my XSLT style sheet.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array" exclude-result-prefixes="#all"
version="3.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="input"/>
<xsl:variable name="newline" select="'
'"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:variable name="input-as-map" select="parse-json($input)" as="map(*)"/>
<root>
<file1>
<xsl:for-each select="$input-as-map?file1?*">
<xsl:value-of select="$newline"/>
<xsl:value-of select="concat(?emp_no,',',count(?emp_no))"/>
<xsl:value-of select="$newline"/>
</xsl:for-each>
</file1>
</root>
</xsl:template>
</xsl:stylesheet>
input json:-
{
"file1": [
{
"emp_no": "0002"
},
{
"emp_no": "0009"
},
{
"emp_no": "0002"
},
{
"emp_no": "0009"
},
{
"emp_no": "0009"
}
]
}
Expected output:-
<root>
<file1>
0002,1
0009,1
0002,2
0009,2
0009,3
</file1>
</root>
Any suggestions also would be helpful.
One solution would be to replace the xsl:for-each with xsl:iterate, maintaining a parameter value containing a map from emp_no to an integer counter.
Another would be to convert the array to XML and use xsl:number.
Or you could use fn:fold-left(), again maintaining a map from emp_no to an integer counter as you step through the array.
None of them particularly easy, I'm afraid: perhaps someone else has a better idea.
Here is Mike's third suggestion spelled out:
<file>
<xsl:value-of
select="fold-left(
parse-json($json)?file1?*,
map{},
function($am, $m) {
let $c := head($am)($m?emp_no),
$new-am := map:put(head($am), $m?emp_no, if (empty($c)) then 1 else $c + 1)
return ($new-am, tail($am), $m?emp_no || ',' || $new-am($m?emp_no))
}
) => tail()"
separator="
"/>
</file>

Date time conversion results in FORG0001 Invalid dateTime value - XSLT 3.0

Resources:
Java 1.8
Saxon-HE-10.3
XSLT3.0
I am merging 2 XML files and want to do date manipulation in the merged output file xml.
I am taking the 2 files as input using java and generate the merged xml - This works fine.
Now, I added the date manipulation in the xsl after the merging operation.
Date Manipulation - convert the given date to epoch/milliseconds
Date format which comes with the input file
<PROP NAME="START_DATE">
<PVAL>16-Aug-2018</PVAL>
</PROP>
The issue is when I try to convert the given date to dateTime or convert to epoch, I get the below error on compilation.
Error at char 10 in expression in xsl:value-of/#select on line 60 column 105 of CASTransform.xsl: FORG0001 Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component) at template reformat-date on line 55 of CASTransform.xsl:
invoked by xsl:call-template at file:/home/Merger/scripts/CASTransform.xsl#50 In template rule with match="element(Q{}RECORDS)/element(Q{}RECORD)/element(Q{}PROP)[(Q{http://www.w3.org/2001/XMLSchema}string(data(attribute::attribute(Q{}NAME)))) eq "START_DATE"]/element(Q{}PVAL)" on line 48 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#45 In template rule with match="(element()|(text()|(comment()|processing-instruction())))" on line 43 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#35 at template main on line 14 of CASTransform.xsl: Exception in thread "main" net.sf.saxon.s9api.SaxonApiException: Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:488)
XSL Code
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="mapData"/>
<xsl:template match="/" name="main">
<RECORDS>
<xsl:variable name="file1" select="map:get($mapData, '1')" />
<xsl:variable name="input-doc1" as="document-node()" select="doc($file1)"/>
<xsl:variable name="file2" select="map:get($mapData, '2')" />
<xsl:variable name="input-doc2" as="document-node()" select="doc($file2)"/>
<xsl:merge>
<xsl:merge-source name="doc1" select="$input-doc1/RECORDS/RECORD">
<xsl:merge-key select="PROP[#NAME = 'Id']/PVAL"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-source name="doc2" select="$input-doc2/RECORDS/RECORD">
<xsl:merge-key select="PROP[#NAME = 'Id']/PVAL"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-action>
<xsl:if test="current-merge-group('doc1')">
<xsl:copy>
<xsl:apply-templates select="*, current-merge-group('doc2')/(* except PROP[#NAME = 'Id'])"/>
</xsl:copy>
</xsl:if>
</xsl:merge-action>
</xsl:merge>
</RECORDS>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="RECORDS/RECORD/PROP[#NAME = 'START_DATE']/PVAL | RECORDS/RECORD/PROP[#NAME = 'END_DATE']/PVAL">
<xsl:element name="{name()}">
<xsl:call-template name="reformat-date">
<xsl:with-param name="date" select="." />
</xsl:call-template>
</xsl:element>
</xsl:template>
<xsl:template name="reformat-date">
<xsl:param name="date" />
<xsl:variable name="dd" select="substring($date,1,2)" />
<xsl:variable name="mmm" select="upper-case(substring($date,4,3))" />
<xsl:variable name="yyyy" select="substring($date,8,4)" />
<xsl:variable name="mmm">
<xsl:choose>
<xsl:when test="$mmm = 'JAN'">01</xsl:when>
<xsl:when test="$mmm = 'FEB'">02</xsl:when>
<xsl:when test="$mmm = 'MAR'">03</xsl:when>
<xsl:when test="$mmm = 'APR'">04</xsl:when>
<xsl:when test="$mmm = 'MAY'">05</xsl:when>
<xsl:when test="$mmm = 'JUN'">06</xsl:when>
<xsl:when test="$mmm = 'JUL'">07</xsl:when>
<xsl:when test="$mmm = 'AUG'">08</xsl:when>
<xsl:when test="$mmm = 'SEP'">09</xsl:when>
<xsl:when test="$mmm = 'OCT'">10</xsl:when>
<xsl:when test="$mmm = 'NOV'">11</xsl:when>
<xsl:when test="$mmm = 'DEC'">12</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="temp1" as="xs:string" select="string-join(($yyyy,$mmm,$dd),'-')" />
<xsl:value-of select="$temp1" />
</xsl:template>
</xsl:stylesheet>
The above XSL code works perfectly fine and the $temp1 gives proper date like 2018-08-16.
Now, when I try to convert this to dateTime variable I get the mentioned compilation error.
Code which gives error:
<xsl:variable name="temp2" as="xs:dateTime" select="xs:dateTime(concat($temp1,'T00:00:00'))"/>
and tried this as well :
<xsl:value-of select="floor((xs:dateTime(concat($temp1,'T00:00:00')) - xs:dateTime('1970-01-01T00:00:00')) div xs:dayTimeDuration('PT1S')) "/>
Requesting your help as this is my first XSL project.
Full Stacktrace:
Error at char 10 in expression in xsl:value-of/#select on line 60 column 105 of CASTransform.xsl:
FORG0001 Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at template reformat-date on line 55 of CASTransform.xsl:
invoked by xsl:call-template at file:/home/Merger/scripts/CASTransform.xsl#50
In template rule with match="element(Q{}RECORDS)/element(Q{}RECORD)/element(Q{}PROP)[(Q{http://www.w3.org/2001/XMLSchema}string(data(attribute::attribute(Q{}NAME)))) eq "START_DATE"]/element(Q{}PVAL)" on line 48 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#45
In template rule with match="(element()|(text()|(comment()|processing-instruction())))" on line 43 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#35
at template main on line 14 of CASTransform.xsl:
Exception in thread "main" net.sf.saxon.s9api.SaxonApiException: Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:488)
at com.tracer.Merger.CASExportInput.trans(CASExportInput.java:38)
at com.tracer.Merger.CASExportInput.main(CASExportInput.java:58)
Caused by: ValidationException: Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at net.sf.saxon.type.ValidationFailure.makeException(ValidationFailure.java:406)
at net.sf.saxon.expr.CastExpression.doCast(CastExpression.java:385)
at net.sf.saxon.expr.CastExpression.evaluateItem(CastExpression.java:402)
at net.sf.saxon.expr.CastExpression.evaluateItem(CastExpression.java:30)
at net.sf.saxon.expr.Expression.iterate(Expression.java:872)
at net.sf.saxon.expr.AtomicSequenceConverter.iterate(AtomicSequenceConverter.java:304)
at net.sf.saxon.expr.Expression.process(Expression.java:949)
at net.sf.saxon.expr.instruct.ValueOf.processLeavingTail(ValueOf.java:340)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:746)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:752)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:264)
at net.sf.saxon.expr.instruct.CallTemplate$CallTemplatePackage.processLeavingTail(CallTemplate.java:549)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:478)
at net.sf.saxon.expr.instruct.ApplyTemplates.apply(ApplyTemplates.java:351)
at net.sf.saxon.expr.instruct.ApplyTemplates.process(ApplyTemplates.java:285)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:352)
at net.sf.saxon.expr.instruct.Copy.processLeavingTail(Copy.java:429)
at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:384)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:568)
at net.sf.saxon.expr.instruct.ApplyTemplates.apply(ApplyTemplates.java:351)
at net.sf.saxon.expr.instruct.ApplyTemplates.process(ApplyTemplates.java:285)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:352)
at net.sf.saxon.expr.instruct.Copy.processLeavingTail(Copy.java:429)
at net.sf.saxon.expr.instruct.Instruction.process(Instruction.java:142)
at net.sf.saxon.expr.parser.ExpressionTool.getIteratorFromProcessMethod(ExpressionTool.java:643)
at net.sf.saxon.expr.instruct.Instruction.iterate(Instruction.java:374)
at net.sf.saxon.expr.instruct.Choose.iterate(Choose.java:1019)
at net.sf.saxon.expr.sort.MergeInstr.lambda$iterate$0(MergeInstr.java:543)
at net.sf.saxon.expr.ContextMappingIterator.next(ContextMappingIterator.java:61)
at net.sf.saxon.om.SequenceIterator.forEachOrFail(SequenceIterator.java:135)
at net.sf.saxon.expr.sort.MergeInstr.processLeavingTail(MergeInstr.java:823)
at net.sf.saxon.expr.instruct.Instruction.process(Instruction.java:142)
at net.sf.saxon.expr.LetExpression.process(LetExpression.java:625)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:352)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:298)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:264)
at net.sf.saxon.trans.XsltController.callTemplate(XsltController.java:850)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:480)
... 2 more
Java code which serves the Input files :
package com.tracer.Merger.CASMerger;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.s9api. * ;
public class CASExportInput {
public static void trans(String XSLFile, String File1, String File2, String OutputFileName, String OutputFilePath) throws SaxonApiException {
String MergedFile = OutputFilePath.concat(OutputFileName);
Processor processor = new Processor(false);
XsltCompiler compiler = processor.newXsltCompiler();
XsltExecutable exp = compiler.compile(new StreamSource(new File(XSLFile)));
Serializer out = processor.newSerializer(new File(MergedFile));
out.setOutputProperty(Serializer.Property.METHOD, "xml");
out.setOutputProperty(Serializer.Property.INDENT, "yes");
Xslt30Transformer trans = exp.load30();
Map < String,
String > mapData = new HashMap < String,
String > ();
mapData.put("1", File1);
mapData.put("2", File2);
HashMap < QName,
XdmValue > parameters = new HashMap < >();
parameters.put(new QName("mapData"), XdmMap.makeMap(mapData));
trans.setStylesheetParameters(parameters);
trans.callTemplate(new QName("main"), out);
System.out.println("Output written to : " + MergedFile);
}
public static void main(String args[]) throws SaxonApiException {
if (args.length < 3) System.out.println("\nPlease check if you have entered arguments properly...");
String XSLFile = args[0].toString().trim();
String File1 = args[1].toString().trim();
String File2 = args[2].toString().trim();
String FileName1 = File1.substring(File1.lastIndexOf("/") + 1, File1.length() - 4);
String FileName2 = File2.substring(File2.lastIndexOf("/") + 1, File2.length() - 4);
String OutputFilePath = File1.substring(0, File1.lastIndexOf("/") + 1);
String OutputFileName = FileName1.concat("-" + FileName2 + ".xml");
trans(XSLFile, File1, File2, OutputFileName, OutputFilePath);
}
}
Not sure what the issue was. But, I uninstalled and re-installed Java 1.8 and the issue got fixed. No changes were done in the xsl or java code.

Convert negative decimal to hexadecimal in xslt

Please, can you help me how to convert negative/positive decimal value to hexadecimal in xslt/xslt 2.0 ?
This is what i tried but does not work with negative numbers/decimals,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="ConvertDecToHex">
<xsl:param name="index" />
<xsl:if test="$index > 0">
<xsl:call-template name="ConvertDecToHex">
<xsl:with-param name="index" select="floor($index div 16)" />
</xsl:call-template>
<xsl:choose>
<xsl:when test="$index mod 16 < 10">
<xsl:value-of select="$index mod 16" />
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$index mod 16 = 10">A</xsl:when>
<xsl:when test="$index mod 16 = 11">B</xsl:when>
<xsl:when test="$index mod 16 = 12">C</xsl:when>
<xsl:when test="$index mod 16 = 13">D</xsl:when>
<xsl:when test="$index mod 16 = 14">E</xsl:when>
<xsl:when test="$index mod 16 = 15">F</xsl:when>
<xsl:otherwise>A</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="my:intToHex(53), my:intToHex(-53)" separator="
"/>
</xsl:template>
<xsl:function name="my:intToHex" as="xs:string">
<xsl:param name="pInt" as="xs:integer"/>
<xsl:variable name="vMinusOneHex64" select="18446744073709551615"/>
<xsl:variable name="vCompl" select=
"if($pInt ge 0)
then $pInt
else $vMinusOneHex64 + $pInt +1"/>
<xsl:sequence select=
"if ($vCompl eq 0)
then '0'
else
concat(if ($vCompl gt 16)
then my:intToHex($vCompl idiv 16)
else '',
substring('0123456789ABCDEF',
($vCompl mod 16) +1,
1)
)"/>
</xsl:function>
</xsl:stylesheet>
When applied on any source XML document (ignored), produces the desired result:
35
FFFFFFFFFFFFFFCB

Calling function for filtering

This function "karusell:isCurrentDateTimeBetweenDates" never gets called. Why cant i use it for filtering? If a call i alone and store it in a variable it work. The value of the variable is false.
<xsl:variable name="bannerList" select="verticaldata/contents/content[ karusell:isCurrentDateTimeBetweenDates( 'Thursday', '01' , 'Friday', '03') ][position() <= 5]" />
Edit:
How can the string be returned?
The filtering
The function
<xsl:function name="karusell:isCurrentDateTimeBetweenDates">
<xsl:param name="startDay"/>
<xsl:param name="startHour" />
<xsl:param name="endDay"/>
<xsl:param name="endHour" />
<xsl:variable name="currentDateTime" select="current-dateTime() " />
<xsl:variable name="todayIndex" select="karusell:getDayIndex(format-dateTime($currentDateTime , '[F]'))" />
<xsl:variable name="startDayIndex" select="karusell:getDayIndex($startDay)" />
<xsl:variable name="endDayIndex" select="karusell:getDayIndex($endDay)" />
<xsl:variable name="startDate" select= "$currentDateTime - ( $todayIndex - $startDayIndex )*xs:dayTimeDuration('P1D')"/>
<xsl:variable name="endDate" select= "$currentDateTime + ( $endDayIndex - $todayIndex )*xs:dayTimeDuration('P1D')"/>
<xsl:variable name="startDateTime" select="format-dateTime($startDate, concat('[Y0001]-[M01]-[D01]T', $startHour ,':00:00')) cast as xs:dateTime"/>
<xsl:variable name="endDateTime" select="format-dateTime($endDate, concat('[Y0001]-[M01]-[D01]T', $endHour ,':00:00')) cast as xs:dateTime"/>
<xsl:value-of select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
</xsl:function>
Use xsl:sequence, not xsl:value-of to return values of the data type your expression has. So replace
<xsl:value-of select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
with
<xsl:sequence select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
Additionally you can get better error diagnosis if you define the return type of your function with <xsl:function name="pf:foo" as="xs:boolean">..</xsl:function>.

Math results in scientific float and cannot calculate with it anymore

I am trying to make an xslt that can convert a position (vector3) via a quaternion to a new position. I have made the following set-up, but I am retrieving NaN's for very small values close to 0. How can I calculate further with the values that come from the quaternion to right-vector calculations?
<xsl:template name="object_markingtape_position">
<xsl:param name="sign"/> <!-- left or right (-1 or 1) -->
<xsl:param name="quaternion"/> <!-- quaternion in x,y,z,w -->
<xsl:variable name="rightvec">
<xsl:call-template name="QuatToRight">
<xsl:with-param name="x" select="$quaternion/ns:x"/>
<xsl:with-param name="y" select="$quaternion/ns:y"/>
<xsl:with-param name="z" select="$quaternion/ns:z"/>
<xsl:with-param name="w" select="$quaternion/ns:w"/>
</xsl:call-template>
</xsl:variable>
Right vector y: <xsl:value-of select="number($rightvec/y)"/> <!-- results in a value with 1.5435434E-04 -->
<xsl:element name="position" namespace="{$ns}">
<xsl:element name="x" namespace="{$ns}">
<xsl:value-of select="$sign * 1.5 * $rightvec/x + ns:position/ns:x"/>
</xsl:element>
<xsl:element name="y" namespace="{$ns}"> <!-- results into NaN -->
<xsl:value-of select="$sign * 1.5 * $rightvec/y + ns:position/ns:y"/>
</xsl:element>
<xsl:element name="z" namespace="{$ns}">
<xsl:value-of select="$sign * 1.5 * $rightvec/z + ns:position/ns:z"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- Functionality for calculating the vectors from a quaternion -->
<!-- From http://nic-gamedev.blogspot.nl/2011/11/quaternion-math-getting-local-axis.html -->
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="vec3">
<xsl:element name="x">
<xsl:value-of select="1 - 2 * ($x * $y - $w * $z)"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="2 * ($x * $y + $w * $z)"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="2 * ($x * $z - $w * $y)"/>
</xsl:element>
</xsl:element>
</xsl:template>
An example of xml values that can come in are the following:
<Item>
<position>
<x>-106.6172</x>
<y>0.780673563</y>
<z>-13.0446815</z>
</position>
<rotation> <!-- this is where the quaternion param is filled with -->
<x>0.0810996</x>
<y>0.354339659</y>
<z>-0.207844481</z>
<w>-0.908111751</w>
</rotation>
</Item>
If you can't find a better solution you might have to resort to using extension functions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:quat="urn:myExtension" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="quat msxsl" version="1.0">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:template match="*">
<xsl:value-of select="quat:calcQuat($x, $y, $z, $w)"/>
</xsl:template>
<msxsl:script implements-prefix="quat" language="c#">
<![CDATA[
public string calcQuat(double x, double y, double z, double w)
{
double result = 1 - 2 * (x * y - w * z)
return Double.ToString(result)
}
]]>
</msxsl:script>
There are three problems I can see:
the variable rightvec contains an XML fragment, but is then used as a node-set - this requires a conversion to work usually (but it depends on the XSLT processor)
the named template object_markingtape_position references an element ns:position in the current context, so its result depends on where it is called - better to pass the position as a parameter;
the named tamplate QuatToRight generates a vec3 element containing sub-elements with the values, but its result is then used as if there was not vec3 intermediate element.
Using the .NET XSLT processor, this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template name="object_markingtape_position">
<xsl:param name="sign"/> <!-- left or right (-1 or 1) -->
<xsl:param name="quaternion"/> <!-- quaternion in x,y,z,w -->
<xsl:param name="position"/> <!-- position in x,y,z -->
<xsl:variable name="rightvecFragment">
<xsl:call-template name="QuatToRight">
<xsl:with-param name="x" select="$quaternion/x"/>
<xsl:with-param name="y" select="$quaternion/y"/>
<xsl:with-param name="z" select="$quaternion/z"/>
<xsl:with-param name="w" select="$quaternion/w"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rightvec" select="msxsl:node-set($rightvecFragment)"/>
<xsl:element name="position">
<xsl:element name="x">
<xsl:value-of select="$sign * 1.5 * $rightvec/x + $position/x"/>
</xsl:element>
<xsl:element name="y">
<!-- results into NaN -->
<xsl:value-of select="$sign * 1.5 * $rightvec/y + $position/y"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="$sign * 1.5 * $rightvec/z + $position/z"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- Functionality for calculating the vectors from a quaternion -->
<!-- From http://nic-gamedev.blogspot.nl/2011/11/quaternion-math-getting-local-axis.html -->
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="x">
<xsl:value-of select="1 - 2 * ($x * $y - $w * $z)"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="2 * ($x * $y + $w * $z)"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="2 * ($x * $z - $w * $y)"/>
</xsl:element>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="object_markingtape_position">
<xsl:with-param name="sign" select="1"/>
<xsl:with-param name="position" select="position"/>
<xsl:with-param name="quaternion" select="rotation"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
applied to the example input produces:
<position>
<x>-104.63717236709732</x>
<y>1.4331220235568977</y>
<z>-12.129909788264223</z>
</position>
I resolved this with some data loss, but it is the cleanest solution so far. I used format-number().
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="vec3">
<xsl:element name="x">
<xsl:value-of select="format-number(1 - 2 * ($x * $y - $w * $z),'0.000000')"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="format-number(2 * ($x * $y + $w * $z),'0.000000')"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="format-number(2 * ($x * $z - $w * $y),'0.000000')"/>
</xsl:element>
</xsl:element>
</xsl:template>