I want to write an xslt to transform one xml file to another. The source XML file is like the following
<orgs>
<organization revenue="10000">
<name>foo</name>
</organization>
<organization parent="foo">
<name>foo2</name>
</organization>
<organization parent="foo2">
<name>foo3</name>
</organization>
</orgs>
The output xml should be as follows
<orgo>
<organization revenue="10000">
<name>foo</name>
<organization>
<name>foo2</name>
<organization><name>foo3</name></organization>
</organization>
</organization>
</orgo>
So far i've tried writing the xsl as follows
This 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="node()">
<xsl:copy>
<xsl:apply-templates select="node()[1]"/>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<orgs>
<organization revenue="10000">
<name>foo</name>
</organization>
<organization parent="foo">
<name>foo2</name>
</organization>
<organization parent="foo2">
<name>foo3</name>
</organization>
</orgs>
produces the wanted, correct output:
<orgs>
<organization>
<name>foo</name>
<organization>
<name>foo2</name>
<organization>
<name>foo3</name>
</organization>
</organization>
</organization>
</orgs>
In case the order of <organization> elements is random, like in the following XML document:
<orgs>
<organization parent="foo2">
<name>foo3</name>
</organization>
<organization parent="foo">
<name>foo2</name>
</organization>
<organization revenue="10000">
<name>foo</name>
</organization>
</orgs>
this transformation produces the wanted result:
<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>
<xsl:apply-templates select="organization[not(#parent)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="organization">
<xsl:copy>
<xsl:copy-of select="node()"/>
<xsl:apply-templates select="../organization[#parent=current()/name]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
In my XML I want to copy values into one node from another node if both nodes have matching values in some other nodes.
<XML>
<ORG>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>X</VAL>
</ITM>
<ITM>
<NUM>2</NUM>
<SEQ>20</SEQ>
<VAL>Y</VAL>
</ITM>
</ORG>
<NEW>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>QQQ</VAL>
</ITM>
</NEW>
</XML>
This works, but for large documents it might be slow, is there a better way?
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ORG/ITM/VAL">
<xsl:variable name="NUM" select="../NUM"/>
<xsl:variable name="SEQ" select="../SEQ"/>
<VAL>
<xsl:value-of select="../../../NEW/ITM/VAL[../NUM=$NUM and ../SEQ=$SEQ]"/>
</VAL>
</xsl:template>
</xsl:stylesheet>
The result has the value "QQQ" in the ORG/ITM/VAL node since ORG/ITM/NUM and ORD/ITM/SEQ match with NEW/ITM/NUM and NEW/ITM/SEQ
<?xml version="1.0"?>
<XML>
<ORG>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>QQQ</VAL>
</ITM>
<ITM>
<NUM>2</NUM>
<SEQ>20</SEQ>
<VAL/>
</ITM>
</ORG>
<NEW>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>QQQ</VAL>
</ITM>
</NEW>
</XML>
The preferred method to lookup values is to define a key and use it. In the given example, this could be:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="new-item" match="NEW/ITM" use="concat(NUM, '|', SEQ)" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ORG/ITM/VAL">
<xsl:copy>
<xsl:value-of select="key('new-item', concat(../NUM, '|', ../SEQ))/VAL"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
using 1 variable is more faster.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ORG/ITM/VAL">
<xsl:variable name="NUMSEQ" select="concat(string(../NUM), string(../SEQ))"/>
<VAL>
<xsl:value-of select="../../../NEW/ITM/VAL[concat(string(../NUM), string(../SEQ))=$NUMSEQ]"/>
</VAL>
</xsl:template>
</xsl:stylesheet>
I have multiple occurence nodes which need to be generated at output using XSLT transformation. Could you please help me on this.
Following XSLT code only generate one node occurrence only. Could you please help me with below XSLT code how to generate multiple nodes elements in Input XML
Input XML
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Body>
<ns1:getGenResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
<ns1:getGenReturn xsi:type="soapenc:Array" soapenc:arrayType="xsd:anyType[2]" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
</ns1:getGenReturn>
</ns1:getGenResponse>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:Gen" xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>
<name xsi:type="xsd:string">ULM</name>
<mail xsi:type="xsd:string">ulm#gmail.com</mail>
</multiRef>
<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns3:Gen" " xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">ABC</name>
<mail xsi:type="xsd:string">abc#gmail.com</mail>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
XSLT Code used for this transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" x
xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:if test="//soap:Body/multiRef">
<xsl:element name="getGenResponse">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="//name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="//mail"/></xsl:element>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- 'Copy ' node -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output from above XSLT
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
/getGenResponse>
Output expected
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
<getGenReturn>
<name>ABC</name>
<mail>abc#gmail.com<mail>
</getGenReturn>
/getGenResponse>
At you moment all you are doing is testing a multiRef element exists, and outputting only one new getGenReturn element.
All you really need to do is replace the xsl:if with xsl:for-each to select all the elements, then you will get one getGenReturn for each. And also change the xsl:value-of to use a relative path
<xsl:template match="/">
<xsl:element name="getGenResponse">
<xsl:for-each select="//soap:Body/multiRef">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="mail"/></xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
Or better still, do this, as xsl:element is not really needed here if you are using static names
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
Note, you don't actually need the identity template in this case. Try this XSLT:
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>
I have the following xml file:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<cases>
<case>
<key>123</key>
</case>
<case>
<key>456</key>
</case>
</cases>
<issues>
<issue>
<issueid>issue1</issueid>
<casekey>123</casekey>
</issue>
<issue>
<issueid>issue2</issueid>
<casekey>123</casekey>
</issue>
<issue>
<issueid>issue3</issueid>
<casekey>456</casekey>
</issue>
</issues>
</root>
I want to move all the <issue> nodes to the <case> whose <casekey> value equal to the <key> value of <case>.
In other words, if the <casekey> value of an <issue> is equal to to the <key> value of <case>, then move that issue under the case.
My final xml should look like below:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<cases>
<case>
<key>123</key>
<issue>
<issueid>issue1</issueid>
<casekey>123</casekey>
</issue>
<issue>
<issueid>issue2</issueid>
<casekey>123</casekey>
</issue>
</case>
<case>
<key>456</key>
<issue>
<issueid>issue3</issueid>
<casekey>456</casekey>
</issue>
</case>
</cases>
</root>
XSLT has a built-in key mechanism for resolving cross-references - it's best to use it:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="issues" match="issue" use="casekey" />
<xsl:template match="/root">
<root>
<cases>
<xsl:for-each select="cases/case">
<case>
<xsl:copy-of select="key"/>
<xsl:copy-of select="key('issues', key)"/>
</case>
</xsl:for-each>
</cases>
</root>
</xsl:template>
</xsl:stylesheet>
P.S. Do not confuse a reference to the key element in your XML with the xsl:key instruction and the key() function.
The main point is a template matching case, including:
Something similar to the identity template.
But before closing xsl:copy there is another xsl:apply-templates,
to process respective issues.
The final thing to add is to block "normal" processing of issues tag.
Below you have a working solution:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="case">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:variable name="kk" select="key"/>
<xsl:apply-templates select="../../issues/issue[casekey=$kk]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="issues"/>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<root>
<xsl:apply-templates select="cases"/>
</root>
</xsl:template>
<xsl:template match="case">
<xsl:variable name="keyvalue" select="normalize-space(key/text())"/>
<case>
<xsl:copy-of select="key"/>
<xsl:if test="ancestor::cases/following-sibling::issues/descendant::casekey = $keyvalue">
<xsl:copy-of
select="ancestor::cases/following-sibling::issues/issue[casekey = $keyvalue]"/>
</xsl:if>
</case>
</xsl:template>
<xsl:template match="issue[child::casekey = preceding::key]"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
I have a case of XSL key grouping. The goal is to replace a value based on an ID match.
Input:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<message-in>
<actions>
<stop>
<action id="33632">
<text>DefaultComment</text>
<planning-status>finished</planning-status>
<realised-times>
<starttime>2017-03-13T12:43:54</starttime>
<finishtime>2017-03-13T13:15:21</finishtime>
</realised-times>
</action>
</stop>
<stop>
<action id="33635">
<planning-status>started</planning-status>
<realised-times>
<starttime>2017-03-13T13:15:21</starttime>
</realised-times>
</action>
</stop>
</actions>
</message-in>
<output_getquerydata>
<queries>
<query name="fat">
<parameters>
<parameter name="id">33632</parameter>
</parameters>
<queryErrors/>
<queryResults>
<record id="1">
<column name="interfaceAction_externalId">33633OREA</column>
</record>
</queryResults>
</query>
</queries>
</output_getquerydata>
<output_getquerydata>
<queries>
<query name="fat">
<parameters>
<parameter name="id">33635</parameter>
</parameters>
<queryErrors/>
<queryResults>
<record id="1">
<column name="interfaceAction_externalId">536313OREA</column>
</record>
</queryResults>
</query>
</queries>
</output_getquerydata>
</root>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:key name="actionKey" match="stop" use="action/#id"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:copy-of select="//message-in"/>
</root>
</xsl:template>
<xsl:template match="action/#id">
<xsl:attribute name="id"><xsl:value-of select="substring-before(//output_getquerydata/queries/query/parameters/parameter[key('actionKey', #id)]/queryResults/record/column[#name='interfaceAction_externalId'],'OREA')"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
I have multiple "action" nodes that have matching "query" nodes.
The goal is that for every action ID we need to replace the ID value from the "action" tag with the corresponding "interfaceAction_externalId" value.
So for action ID 33632 we'll copy and replace with the value of "33633" (because we have a match in parameneter/name/#id 33632 = action/#id ).
The copy works well I get all the information I need, but it seems that action/#id doesn't get replaced.
I thought that I'll use a key to save the values of action/#id then use that in the template match to replace with the value from interfaceAction_externalId, but it doesn't work and I'm not sure what I'm doing wrong.
Thanks for your help!
The whole approach only makes sense if you use <xsl:apply-templates select="//message-in"/> instead of <xsl:copy-of select="//message-in"/>.
As for using a key, I think you want
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:key name="ref-query"
match="output_getquerydata/queries/query/queryResults/record/column[#name='interfaceAction_externalId']"
use="ancestor::query/parameters/parameter[#name = 'id']"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:apply-templates select="//message-in"/>
</root>
</xsl:template>
<xsl:template match="action/#id">
<xsl:attribute name="id"><xsl:value-of select="substring-before(key('ref-query', .),'OREA')"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
I am newbie to XSLT.can you help me for xslt to achieve below output. In my input xml have duplicate nodes and i have to remove based on element (CustAccId) value should not be re-peat.
Inputxml :
<Main>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
</Main>
Output XML :
<Main>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
Here is XSLt i tried but didn't work like it retrun duplicate request node
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:if test="not(following::Request[CustAcctID=current()])">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
You can remove the duplicate with following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Request[CustAcctID = following::Request/CustAcctID]"/>
</xsl:stylesheet>
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<Main>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
</Main>
The template matching all Request nodes where the CustAcctID matches the CustAcctID of the following Request will not produce any output for the matching Request, so the duplicates will not be written.
Update for the advice in the comment by michael.hor257k: Another approach is to use Muenchian grouping:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="x" match="ServiceOrderID" use="." />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Request">
<xsl:for-each select=".">
<xsl:if test="generate-id(ServiceOrderID) =
generate-id(key('x', ServiceOrderID)[1])">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
which produces the same output XML but can be more efficient as you'll find described in detail in the article by Jeni Tennison http://www.jenitennison.com/xslt/grouping/muenchian.xml that michael.hor257k already recommended.
As additional reference for XSLT grouping you can have a look at http://www.dpawson.co.uk/xsl/sect2/N4486.html