Problems wits xslt processor in WSO2 - xslt

In wso2 esb 4.9.0 I defined next xslt mediator:
<localEntry key="TestXslt">
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="param1">
<FileList>
<File Name="b" automated="false" valid="true"/>
<File Name="c" automated="false" valid="true"/>
<File Name="d" automated="false" valid="true"/>
<File Name="e" automated="false" valid="true"/>
<File Name="f" automated="false" valid="true"/>
</FileList>
</xsl:param>
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="$param1/FileList/File">
<qq>TEST</qq>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<description/>
</localEntry>
This xslt just shoul create 5 xml tags. Expected output:
<qq>TEST</qq>
<qq>TEST</qq>
<qq>TEST</qq>
<qq>TEST</qq>
<qq>TEST</qq>
But when i call this xslt mediator, my message is empty.
What i may do wrong. May be i should activate XSLT 2.0. How can i do it?

Inside WSO2, the param you pass to your TestXslt with xslt mediator become a string.
Why can't you work with the current payload message, what are you trying to do ?
If you need current message's XML payload + an other xml source (loaded from the registry for exemple) in your xsl, you can add this xml fragment inside the current message before calling xslt mediator. Use enrich mediator exemple :
<enrich>
<source clone="true" xpath="get-property('some_external_xml')"/>
<target type="body" action="child"/>
</enrich>

Related

WSo2 Time converter for 12 to 24 hour format

How to convert 12 hour format time in to 24 hour Format as a Property in the WSO2 ESB
sample time : 6:00PM
Converted time : 1800
WSO2 does not have an OOB solution for this. But there are multiple ways to implement this.
Using XSLT mediator.
You can use the following XSLT to convert your time to 24Hours format. Note that the following XSLT is tightly coupled to the sample formats you provided. If you have a different format you may need to alter the XSLT accordingly.
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="LOCAL_XSLT_Time" xmlns="http://ws.apache.org/ns/synapse">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="yes"/>
<xsl:param name="timeToConv"/>
<xsl:template match="/">
<!-- Iterating through message elements -->
<time>
<xsl:call-template name="time-to-24H">
<xsl:with-param name="timeToConvert" select="$timeToConv"/>
</xsl:call-template>
</time>
</xsl:template>
<xsl:template name="time-to-24H">
<xsl:param name="timeToConvert"/>
<xsl:variable name="hours" select="substring-before($timeToConvert, ':')"/>
<xsl:variable name="minWithComp" select="substring-after($timeToConvert, ':')"/>
<xsl:variable name="min" select="substring($minWithComp, 1, string-length($minWithComp) - 2)"/>
<xsl:variable name="comp" select="substring($minWithComp, string-length($minWithComp) - 1, string-length($minWithComp))"/>
<xsl:variable name="hourPart" select="floor($hours mod 12 + 12 * number($comp='PM'))"/>
<xsl:value-of select="$hourPart"/>
<xsl:value-of select="$min"/>
</xsl:template>
</xsl:stylesheet>
</localEntry>
You can invoke this XSLT using the XSLT mediator. Read more on the XSLT mediator from here.
<xslt key="LOCAL_XSLT_Time">
<property name="timeToConv" value="6:00PM"/>
</xslt>
Then after the XSLT mediator, you can access converted time like below.
<property name="convertedTime" expression="//time/text()"></property>
This question has more details on how to use the XSLT mediator.
2 Using the script mediator.
You can use the Script mediator with JavaScript to convert your time. There are plenty of JavaScript samples out there for this. You can refer this to understand how to use the script mediator.

XSLT Template Matching from xml

I have the following xml code that I need to transform into a text file. I'm struggling massively with the namespaces in XSLT. I'm used to doing Export/Record and simple default namespace matches. I have the following XML code:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfPerson xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MultiCountryIntegrationService.Core.Entities.Dto">
<Person>
<Addresses>
<Address>
<City>London</City>
<Country>GB</Country>
<County>London</County>
<CreatedDate xmlns:d5p1="http://schemas.datacontract.org/2004/07/System">
<d5p1:DateTime>2017-02-21T11:05:08.8387752Z</d5p1:DateTime>
<d5p1:OffsetMinutes>0</d5p1:OffsetMinutes>
</CreatedDate>
<DeletedDate xmlns:d5p1="http://schemas.datacontract.org/2004/07/System" i:nil="true" />
<EndDate xmlns:d5p1="http://schemas.datacontract.org/2004/07/System" i:nil="true" />
<Extension i:nil="true" />
<Id>8e5b30d0</Id>
<ModifiedDate xmlns:d5p1="http://schemas.datacontract.org/2004/07/System" i:nil="true" />
<Number>8</Number>
<StartDate xmlns:d5p1="http://schemas.datacontract.org/2004/07/System">
<d5p1:DateTime>2016-06-30T22:00:00Z</d5p1:DateTime>
<d5p1:OffsetMinutes>120</d5p1:OffsetMinutes>
</StartDate>
<Street>Somewhere</Street>
<Type>Primary</Type>
<ZipCode>L1 1LL</ZipCode>
</Address>
</Addresses>
<PersonLocalReferences xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</Person>
<Person>
<Addresses>
<Address>
<City>Birmingham</City>
<Country>ETC...</Country>
</Address>
</Addresses>
<PersonLocalReferences xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</Person>
</ArrayOfPerson>
My XSLT - As you can see I've tried several approaches, done countless hours on google and stackoverflow. If I remove the i: and xmlns from the XML I can get the stylesheet to work, but I'm not in a position to change the XML. I'm using Visual Studio 2016 to create and run the xslt. If I use "#* | node()" I get everything, and I only want to output certain bits of information into a text file.
If I run the xslt below, I just get the header and EOF, so it looks like the <xsl:apply-templates select="ArrayOfPerson/Person"/> isn't selecting the right level of data.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="i">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:call-template name="header" />
<xsl:text>Person</xsl:text>
<xsl:apply-templates select="ArrayOfPerson/Person"/>
<xsl:value-of select="$newline" />
<xsl:text>EOF</xsl:text>
</xsl:template>
<xsl:template match="Person">
<xsl:value-of select="ArrayOfPerson/Person/Addresses/Address/City"/>
<xsl:value-of select="Person/Addresses/Address/City"/>
<xsl:value-of select="Addresses/Address/City"/>
<xsl:value-of select="."/>
<xsl:value-of select="$newline" />
<xsl:text>match</xsl:text>
<xsl:value-of select="$newline" />
</xsl:template>
</xsl:stylesheet>
As Michael wrote in his comment, add xpath-default-namespace="http://schemas.datacontract.org/2004/07/MultiCountryIntegrationService.Core.Entities.Dto"
to your xsl:stylesheet tag.
Note that you must include full namespace.

XSLT Transformation for attached XML

I have a XML as shown below:
<NikuDataBus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/nikuxog_project.xsd">
<Header action="write" externalSource="NIKU" objectType="project" version="14.3.0.298"/>
<Projects>
<Project projectID="0000000284" projectName="Testing">
<Tasks>
<Task internalTaskID="5095022" name="PS2" taskID="PS2"> </Task>
<Task internalTaskID="5095038" name="PS" taskID="PS"> </Task>
</Tasks>
<Allocations/>
<scenarioDependencies/>
<InvestmentAssociations>
<Allocations/>
<Hierarchies/>
</InvestmentAssociations>
<CustomInformation/>
<General/>
<OBSAssocs/>
</Project>
</Projects>
</NikuDataBus>
I want this XML to be transformed using XSLT and the end result should be only as shown below (only the Tasks tag). How can my XSLT be?
<Tasks>
<Task internalTaskID="5095022" name="PS2" taskID="PS2"> </Task>
<Task internalTaskID="5095038" name="PS" taskID="PS"> </Task>
</Tasks>
You can select and copy that element with e.g.
<xsl:template match="/">
<xsl:copy-of select="//Tasks" copy-namespaces="no"/>
</xsl:template>
Note that copy-namespaces="no" is XSLT 2.0, without it or in XSLT 1.0 a simple copy will copy the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" as it is in scope from the root element.

A sequence of more than one item is not allowed as the second argument of concat()

The below xsl works fine if I do not bring in the "other_location_postal_code" field, which is commented here.
This is because there are multiple records if I bring in that field.
How can I have this xsl evaluate each record so it would write this record twice, once for the one "other location postal code" and the other?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:e="http://www.taleo.com/ws/tee800/2009/01" xmlns:fct="http://www.taleo.com/xsl_functions" exclude-result-prefixes="e fct">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:param name="OUTBOUND_FOLDER"/>
<xsl:template match="/">
<source>
<xsl:apply-templates select="//e:Requisition"/>
</source>
</xsl:template>
<xsl:template match="e:Requisition">
<xsl:variable name="job_id" select="e:ContestNumber"/>
<xsl:variable name="other_location_postal_code" select="e:JobInformation/e:JobInformation/e:OtherLocations/e:Location/e:NetworkLocation/e:NetworkLocation/e:ZipCode"/>
<job>
<job_id>
<xsl:value-of select="concat('<','![CDATA[',$job_id,']]','>')"/>
</job_id>
<other_location_postal_code>
<xsl:value-of select="concat('![CDATA[',$other_location_postal_code,']]')"/>
</other_location_postal_code>
</job>
</xsl:template>
</xsl:stylesheet>
I want it to come out like so:
<?xml version="1.0" encoding="UTF-8"?>
<source>
<job>
<job_id><![CDATA[15000005]]></job_id>
<other_location_postal_code><![CDATA[77382]]></other_location_postal_code>
</job>
<job>
<job_id><![CDATA[15000005]]></job_id>
<other_location_postal_code><![CDATA[37567]]></other_location_postal_code>
</job>
</source>
The initial XML looks like so:
<?xml version="1.0" encoding="UTF-8"?>
-<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
-<soapenv:Body>
-<ns1:getDocumentByKeyResponse xmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
-<Document xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
-<Attributes>
<Attribute name="count">1</Attribute>
<Attribute name="duration">0:00:00.088</Attribute>
<Attribute name="entity">Requisition</Attribute>
<Attribute name="mode">T-XML</Attribute>
<Attribute name="version">http://www.taleo.com/ws/tee800/2009/01</Attribute>
</Attributes>
-<Content>
-<ExportTXML xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07" xmlns:e="http://www.taleo.com/ws/tee800/2009/01">
-<e:Requisition>
<e:ContestNumber>15000005</e:ContestNumber>
-<e:JobInformation>
-<e:JobInformation>
-<e:OtherLocations>
-<e:Location>
<e:ZipCode>77002</e:ZipCode>
</e:Location>
-<e:Location>
<e:ZipCode>77050</e:ZipCode>
</e:Location>
</e:OtherLocations>
</e:JobInformation>
</e:JobInformation>
</e:Requisition>
</ExportTXML>
</Content>
</Document>
</ns1:getDocumentByKeyResponse>
</soapenv:Body>
</soapenv:Envelope>
The error message simply says that an argument to the concat function is a sequence of more than one node. So some of your variable selects two or more nodes and concat expects as single node for each of its arguments. You will need to decide what you want to output, either only the first node with $var[1] or the concatenation with string-join($var, ' ').
Note that you don't need all those attempts to output CDATA section, you can tell the XSLT processor the cdata-section-elements on the xsl:output direction.
As you have now posted more details here is one suggestion to map each ZipCode element to job element:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:e="http://www.taleo.com/ws/tee800/2009/01"
exclude-result-prefixes="e">
<xsl:output indent="yes" cdata-section-elements="job_id other_location_postal_code"/>
<xsl:template match="/">
<source>
<xsl:apply-templates select="//e:OtherLocations/e:Location/e:ZipCode"/>
</source>
</xsl:template>
<xsl:template match="e:ZipCode">
<xsl:apply-templates select="ancestor::e:Requisition">
<xsl:with-param name="zc" select="current()"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="e:Requisition">
<xsl:param name="zc"/>
<job>
<job_id><xsl:value-of select="e:ContestNumber"/></job_id>
<other_location_postal_code><xsl:value-of select="$zc"/></other_location_postal_code>
</job>
</xsl:template>
</xsl:stylesheet>

xslt, fn:subsequance, java transformation

I have XML data
<logData>
<log>
<id>1</id>
</log>
<log>
<id>2</id>
</log>
<log>
<id>3</id>
</log>
<log>
<id>4</id>
</log>
</logData>
I want get only part of logs using xslt transformation using fn:subsequence function
here is my xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:fn="http://www.w3.org/2006/xpath-functions" version="1.0" >
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/logData" >
<xsl:element name="log">
<xsl:copy-of select="fn:subsequence(./log, 2, 3)"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
and I get
ERROR: 'The first argument to the non-static Java function 'subsequence' is not a valid object reference.'
I am using Java transformation API, part of Java SE 1.6.
Can you help me?
<xsl:copy-of select="fn:subsequence(./log, 2, 3)"/>
The function subsequence() is defined in XPath 2.0 and is available only in an XSLT 2.0 processor.
In XSLT 1.0 use:
<xsl:copy-of select="log[position() > 1 and not(position() > 4)]"/>
Here is a complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy-of select="log[position() > 1 and not(position() > 4)]"/>
</xsl:template>
</xsl:stylesheet>
When this is applied on the provided XML document:
<logData>
<log>
<id>1</id>
</log>
<log>
<id>2</id>
</log>
<log>
<id>3</id>
</log>
<log>
<id>4</id>
</log>
</logData>
the wanted, correct result is produced:
<log>
<id>2</id>
</log>
<log>
<id>3</id>
</log>
<log>
<id>4</id>
</log>
Since you are using Java, all you need to do is to ensure that your code loads an XSLT 2.0 processor instead of XSLT 1.0. The default XSLT processor in the JDK only supports XSLT 1.0.
Download Saxon-HE, and set the system property
-Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl
and your code should work.
(Of course, as Dimitre shows, this transformation can be done easily enough in XSLT 1.0. But by sticking to XSLT 1.0 you are are trying to move with your feet tied together at the ankles. XSLT 2.0 is vastly more powerful and easier to use, and it's available in your environment, so use it.)