XSLT Transformation for attached XML - xslt

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.

Related

Problems wits xslt processor in WSO2

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>

xslt date conversion to UTC

I am very new to xslt programming. Can any one help with this:
input xml:
<?xml version="1.0" encoding="UTF-8"?>
<MESSAGE>
<ER>
<MXITEMOUT xmlns="http://www.mro.com/mx/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" language="EN">
<Header operation="Notify" event="1">
<SenderID type="MAXIMO" majorversion="6" minorversion="0" build="02" dbbuild="V600-76">MX</SenderID>
<CreationDateTime>2005-08-15T14:28:06-05:00</CreationDateTime>
<RecipientID>EXTSYS1234</RecipientID>
<MessageID>1124</MessageID>
</Header>
<Content>
<MXITEM>
<ITEM action="Add">
<ITEMNUM>I1001</ITEMNUM>
<DESCRIPTION langenabled="1">test item</DESCRIPTION>
<ROTATING>1</ROTATING>
<LOTTYPE maxvalue="NOLOT">NOLOT</LOTTYPE>
<CAPITALIZED>0</CAPITALIZED>
<CREATEDDATE>2014-05-22T13:00:46+10:00</CREATEDDATE>
</ITEM>
</MXITEM>
</Content>
</MXITEMOUT>
</ER>
<IR>
<MXITEMOUT xmlns="http://www.mro.com/mx/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" language="EN">
<Header operation="Notify" event="1">
<SenderID type="MAXIMO" majorversion="6" minorversion="0" build="02" dbbuild="V600-76">MX</SenderID>
<CreationDateTime>2005-08-15T14:28:06-05:00</CreationDateTime>
<RecipientID>EXTSYS1234</RecipientID>
<MessageID>1124</MessageID>
</Header>
<Content>
<MXITEM>
<ITEM action="Add">
<ITEMNUM>I1001</ITEMNUM>
<DESCRIPTION langenabled="1">test item</DESCRIPTION>
<ROTATING>1</ROTATING>
<LOTTYPE maxvalue="NOLOT">NOLOT</LOTTYPE>
<CAPITALIZED>0</CAPITALIZED>
<CREATEDDATE>2014-05-22T13:00:46+10:00</CREATEDDATE>
</ITEM>
</MXITEM>
</Content>
</MXITEMOUT>
</IR>
</MESSAGE>
My XSLT is:
<?xml version="1.0"?>
<!--
This XSL is supposed to change the ITEMNUM tag value by prepending 001 to the existing value.
It also strips off the MESSAGE and IR wrapper tags so that the resultant data is consistent with
our XML schema
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mro="http://www.mro.com/mx/integration" version="1.0" exclude-result-prefixes="mro">
<xsl:template match="/MESSAGE">
<xsl:apply-templates select="IR"/>
</xsl:template>
<xsl:template match="IR">
<xsl:apply-templates select="#*|*|text()"/>
</xsl:template>
<xsl:template match="#*|*|text()">
<xsl:copy>
<xsl:apply-templates select="#*|*|text()"/>
</xsl:copy>
</xsl:template>
<!--
Change the ITEMNUM tag value by prepending 001 to the existing value
-->
<xsl:template match="mro:ITEMNUM">
<xsl:element name="ITEMNUM" namespace="http://www.mro.com/mx/integration">
<xsl:text>MAX-</xsl:text><xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Current output is:
<?xml version="1.0" encoding="UTF-8"?>
<MXITEMOUT xmlns="http://www.mro.com/mx/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" language="EN">
<Header operation="Notify" event="1">
<SenderID type="MAXIMO" majorversion="6" minorversion="0" build="02" dbbuild="V600-76">MX</SenderID>
<CreationDateTime>2005-08-15T14:28:06-05:00</CreationDateTime>
<RecipientID>EXTSYS1234</RecipientID>
<MessageID>1124</MessageID>
</Header>
<Content>
<MXITEM>
<ITEM action="Add">
<mro:ITEMNUM xmlns:mro="http://www.mro.com/mx/integration">MAX-I1001</mro:ITEMNUM>
<DESCRIPTION langenabled="1">test item</DESCRIPTION>
<ROTATING>1</ROTATING>
<LOTTYPE maxvalue="NOLOT">NOLOT</LOTTYPE>
<CAPITALIZED>0</CAPITALIZED>
<CREATEDDATE>2014-05-22T13:00:46+10:00</CREATEDDATE>
</ITEM>
</MXITEM>
</Content>
</MXITEMOUT>
Expected output is:
<?xml version="1.0" encoding="UTF-8"?>
<MXITEMOUT xmlns="http://www.mro.com/mx/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" language="EN">
<Header operation="Notify" event="1">
<SenderID type="MAXIMO" majorversion="6" minorversion="0" build="02" dbbuild="V600-76">MX</SenderID>
<CreationDateTime>2005-08-15T14:28:06-05:00</CreationDateTime>
<RecipientID>EXTSYS1234</RecipientID>
<MessageID>1124</MessageID>
</Header>
<Content>
<MXITEM>
<ITEM action="Add">
<mro:ITEMNUM xmlns:mro="http://www.mro.com/mx/integration">MAX-I1001</mro:ITEMNUM>
<DESCRIPTION langenabled="1">test item</DESCRIPTION>
<ROTATING>1</ROTATING>
<LOTTYPE maxvalue="NOLOT">NOLOT</LOTTYPE>
<CAPITALIZED>0</CAPITALIZED>
<CREATEDDATE>2014-05-22T23:00:46.000Z</CREATEDDATE>
</ITEM>
</MXITEM>
</Content>
</MXITEMOUT>
I am trying find logic in google to convert date to utc format in xslt.
It is throwing strange errors.
XML date:2014-05-22T13:00:46+10:00 Expected Date: 2014-05-22T23:00:46.000Z

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>

XML node template - should I use XSLT?

I've got an XML file with the following structure (multiple "entity" nodes):
<!-- entities.xml -->
<root>
<entity template="foo-template" kind="foo" name="bar">
<groups>
<group id="1">
<definition id="1" name="foobar" />
</group>
</groups>
</entity>
</root>
Many entity nodes have similar attributes and children nodes. I'd like to allow users to create entity templates in a separate file. Referencing the template will be done as follows:
<entity template="foo-template" kind="foo" ... />
Every attribute and child node from "foo-template" should be copied into the entity, except for those that already exist (i.e. allow overriding the template).
I'm not very familiar with XSLT. Is it the right tool for this task, or am I better off implementing this without it?
I'm using C++ and RapidXml, but can use other XML libraries.
Edit: example.
Template file:
<!-- templates.xml -->
<templates>
<entity template="foo-template" name="n/a" model="baz">
<groups>
<group id="1">
<definition id="1" name="def1" />
<definition id="2" name="def2" />
</group>
<group id="2">
<definition id="1" name="def3" />
<definition id="2" name="def4" />
</group>
</groups>
</entity>
</templates>
Output file:
<!-- output.xml -->
<root>
<entity kind="foo" name="bar" model="baz">
<groups>
<group id="1">
<definition id="1" name="foobar" />
</group>
<group id="2">
<definition id="1" name="def3" />
<definition id="2" name="def4" />
</group>
</groups>
</entity>
</root>
So the output contains group 1 from "entities.xml" and group 2 from "templates.xml". No need to merge group nodes with the same id.
If you have a file templates.xml that looks like
<templates>
<entity template="foo-template" kind="foo" name="bar" model="baz" />
<!-- and other entity elements with different template="..." values -->
</templates>
then an XSLT such as the following would achieve what you're after
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="kEntityTemplate" match="entity" use="#template" />
<!-- identity template - copy everything not overridden by another template -->
<xsl:template match="#*|node">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
<xsl:template match="entity[#template]">
<xsl:variable name="thisEntity" select="." />
<!-- switch to templates doc -->
<xsl:for-each select="document('templates.xml')">
<xsl:variable name="template"
select="key('kEntityTemplate', $thisEntity/#template)" />
<entity>
<!-- copy template attributes that are not overridden -->
<xsl:for-each select="$template/#*">
<xsl:if test="not($thisEntity/#*[name() = name(current())])">
<!-- if not, copy the one from the template -->
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
<!-- copy source attributes -->
<xsl:apply-templates select="$thisEntity/#*[name() != 'template']" />
<!-- deal with elements -->
<xsl:if test="$thisEntity/groups/group | $template/groups/group">
<groups>
<!-- here we select all group elements from the source plus
those group elements from the template that do not also exist
in the source, and sort the whole lot by id -->
<xsl:apply-templates select="$thisEntity/groups/group
| $template/groups/group[not(#id = $thisEntity/groups/group/#id)]">
<xsl:sort select="#id" data-type="number" />
</xsl:apply-templates>
</groups>
</xsl:if>
</entity>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The templates.xml file needs to be in the same directory as the stylesheet.
One option that you have outside of doing any kind of XML transformation is importing the other XML file and then referencing it from within the tags. See here for an example.
This would require your users to have separate template files for each template type which you may not want. However I would prefer the import approach because of the kiss principle. If you're not familiar with XSLT then importing is probably a better way to go as well.
I hope this helps!

Two phase XSLT transformation converting string to XML first

I have the following XML:
<?xml version="1.0" encoding="utf-8"?>
<string>
<Table>
<Rows>
<Row Id="0">
<Column Name="INS_NAME" XPath="Ins.Name">Jane</Column>
<Column Name="INS_LASTNAME" XPath="Ins.LastName">Smith</Column>
</Row>
<Row Id="1">
<Column Name="INS_NAME" XPath="Ins.Name">Joe</Column>
<Column Name="INS_LASTNAME" XPath="Ins.LastName">Miller</Column>
</Row>
<Row Id="2">
<Column Name="INS_NAME" XPath="Ins.Name">George</Column>
<Column Name="INS_LASTNAME" XPath="Ins.LastName">Ramsey</Column>
</Row>
</Rows>
</Table>
</string>
and I would like to transform it to this XML using a single XSLT:
<?xml version="1.0" encoding="utf-8"?>
<Customers>
<Customer><Name>Jane</Name><LastName>Smith</LastName></Customer>
<Customer><Name>Joe</Name><LastName>Miller</LastName></Customer>
<Customer><Name>George</Name><LastName>Ramsey</LastName></Customer>
</Customers>
I can do it with two different XSLT's:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="/" disable-output-escaping="yes" />
</xsl:template>
</xsl:stylesheet>
and then:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Customers>
<xsl:for-each select="Table/Rows/Row">
<Customer>
<Name><xsl:value-of select="Column[#Name='INS_NAME']" /></Name>
<LastName><xsl:value-of select="Column[#Name='INS_LASTNAME']" /></LastName>
</Customer>
</xsl:for-each>
</Customers>
</xsl:template>
</xsl:stylesheet>
I have been reading about multi phase transformations but I can't seem to get it. I have tried saving the first XSLT in a variable but it seems disable-output-escaping="yes" does not work when saving to a variable.
Can anybody help?
Thank you.
New information (Edit)
I am now translating the string this way:
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="stringXml">
<?xml version="1.0" encoding="utf-8"?>
<xsl:value-of select="translate(translate(/,'>','>'),'<','<')" />
</xsl:variable>
...
How can I do a transformation on the resulting XML stored in stringXML?
Final Solution (Edit)
<msxml:script implements-prefix="myLib" language="C#">
<msxml:assembly name="System.Web"/>
<msxml:using namespace="System.Web"/>
<![CDATA[
public System.Xml.XPath.XPathNodeIterator convertText(string text)
{
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.LoadXml(text);
return doc.CreateNavigator().Select("/");
}
]]>
</msxml:script>
it seems disable-output-escaping="yes" does not work when saving to a
variable.
Your observation is correct.
DOE only affects the serialization of the (final) result of the transformation and isn't applied on intermediary trees.
Here is what the W3C XSLT 1.0 specification explicitly says:
"An XSLT processor will only be able to disable output escaping if it
controls how the result tree is output. This may not always be the
case. For example, the result tree may be used as the source tree for
another XSLT transformation instead of being output."
The same negative answer holds for trying to use a variable, whose value is a string, containing a textual representation of an XML document.
I had a similar situation where I needed to parse an escaped XML inside my actual XML. I will post up my solution to also help someone else. Please also note that I am also using Saxon-PE parser.
In my situation I have the original XML that contains an escaped XML in a child node. I needed to get the inner XML inside the RootNode of the escaped XML.
Source XML:
<?xml version="1.0" encoding="utf-8"?>
<MyTestXml>
<SomeXmlStuff>
<Text1>Hello</Text1>
<Text2>World</Text2>
</SomeXmlStuff>
<SomeEscapedXml><RootNode><FirstNode>Hello</FirstNode><SecondNode>World</SecondNode><ThirdNode>Again</ThirdNode></RootNode></SomeEscapedXml>
</MyTestXml>
When you unescaped the XML, it looks like this:
<RootNode>
<FirstNode>Hello</FirstNode>
<SecondNode>World</SecondNode>
<ThirdNode>Again</ThirdNode>
</RootNode>
With the following XSLT transformation is applied on the source XML:
<?xml version='1.0' encoding='utf-8' ?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/"
exclude-result-prefixes="xsl saxon">
<xsl:template match="/">
<MyOutput>
<xsl:call-template name="GetRootNodeInnerXml">
<xsl:with-param name="escapedXml" select="MyTestXml/SomeEscapedXml" />
</xsl:call-template>
</MyOutput>
</xsl:template>
<xsl:template name="GetRootNodeInnerXml">
<xsl:param name="escapedXml" required="yes" />
<xsl:copy-of select="saxon:parse($escapedXml)/RootNode/node()"/>
<!-- You can also use this line below if you're not using saxon parser. Just make sure your parser supports XSL 3.0 -->
<!--
<xsl:copy-of select="fn:parse-xml($escapedXml)/RootNode/node()" xmlns:fn="http://www.w3.org/2005/xpath-functions"/>
-->
</xsl:template>
</xsl:stylesheet>
This gives you the following output:
<?xml version='1.0' ?>
<MyOutput>
<FirstNode>Hello</FirstNode>
<SecondNode>World</SecondNode>
<ThirdNode>Again</ThirdNode>
</MyOutput>