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