How to merge two xml file using xslt1.0? - xslt

File1.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<data>
<title>Title1</title>
<description>Description1</description>
<myid>1</myid>
</data>
<data>
<title>Title2</title>
<description>Description2</description>
<myid>2</myid>
</data>
</catalog>
File2.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<data>
<author>Author1</author>
<date>12/34/5678</date>
<myid>1</myid>
</data>
<data>
<author>Author2</author>
<date>87/65/4321</date>
<myid>2</myid>
</data>
</catalog>
need output like below using xslt1.0
<catalog>
<data>
<title>Title1</title>
<description>Description1</description>
<myid>1</myid>
<author>Author1</author>
<date>12/34/5678</date>
</data>
<data>
<title>Title2</title>
<description>Description2</description>
<myid>2</myid>
<author>Author2</author>
<date>87/65/4321</date>
</data>
</catalog>

You need to use the document() function, like so:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<xsl:template match="/">
<catalog>
<!-- Apply to all data elements in file 1 -->
<xsl:apply-templates select="document('file1.xml')/catalog/data" />
</catalog>
</xsl:template>
<xsl:template match="data">
<data>
<!--Use myid as a lookup-->
<xsl:variable name="myId" select="myid/text()" />
<!--copy all data child nodes from file1-->
<xsl:copy-of select="#* | node()"/>
<!--copy all data child nodes from file2, excluding myid as we already have it-->
<xsl:copy-of select="document('file2.xml')/catalog/data[myid=$myId]/*[not(local-name()='myid')]"/>
</data>
</xsl:template>
</xsl:stylesheet>

Related

change element name with attribute value using XSLT

I want to change element name with attribute value
My input xml looks like below
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<ROW id="1">
<D n="ord_code">10033</D>
<D n="po_commts">Part and Service Requisition Example</D>
<D n="createddt">2021-02-11 00:00:00.0</createddt>
</ROW>
<ROW id="2">
<D n="ord_code">10018</D>
<D n="po_commts">GL Test</D>
<D n="createddt">2021-02-04 00:00:00.0</D>
</ROW>
</Envelope>
my expected result child element name replaced with attribute value
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<ROW id="1">
<ord_code>10033</ord_code>
<po_commts>Part and Service Requisition Example</po_commts>
<createddt>2021-02-11 00:00:00.0</createddt>
</ROW>
<ROW id="2">
<ord_code>10018</ord_code>
<po_commts>GL Test</po_commts>
<createddt>2021-02-04 00:00:00.0</createddt>
</ROW>
</Envelope>
Here's how you could do it:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="D">
<xsl:element name="{#n}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/gVrvcy2

Splitting Multiline XML tag into multiple Nodes

I have a XML below, where new lines are added after each line at Note__c tag. I need to produce the XML by splitting them into multiple Note__c tags.
Input XML-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Note__c>01/15/2020
123456
DFGRTE766
6tgBFR</Note__c>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
Where Note__c contains multiple strings with new line added after each(except the last one)
Expected Output -
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c>123456</Note__c>
<Note__c>DFGRTE766</Note__c>
<Note__c>6tgBFR</Note__c>
</Notes>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
I have written this XSLT but it is missing few tags under the payload element -
<xsl:stylesheet version="2.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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload">
<Notes>
<xsl:for-each select="tokenize(Note__c,'\n')">
<Note__c>
<xsl:value-of select="."/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
Output of this-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c> 123456</Note__c>
<Note__c> DFGRTE766</Note__c>
<Note__c> 6tgBFR</Note__c>
</Notes>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
not sure what is missing.
Thanks
Sugata
Change your XSLT to
<xsl:stylesheet version="2.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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload/Note__c">
<Notes>
<xsl:for-each select="tokenize(.,'\n')">
<Note__c>
<xsl:value-of select="normalize-space(.)"/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

Grouping and Sum of XML nodes using xslt

I am pretty new in XSLT and I am stuck at once place.
I have one input xml file as below
<Roots>
<Root>
<Value1>Links1</Value1>
<Value2>notadmin</Value2>
<Count>1</Count>
</Root>
<Root>
<Value1>Links1</Value1>
<Value2>notadmin</Value2>
<Count>10</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>userxyz</Value2>
<Count>10</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>usermnp</Value2>
<Count>10</Count>
</Root>
<Root>
<Value1>Links3</Value1>
<Value2>user123</Value2>
<Count>5</Count>
</Root>
...
...
...
</Roots>
Now, I want to sum the count value for similar values of Value1 and Value2 so that it looks like below:
<Roots>
<Root>
<Value1>Links1</Value1>
<Value2>notadmin</Value2>
<Count>11</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>userxyz</Value2>
<Count>20</Count>
</Root>
<Root>
<Value1>Links3</Value1>
<Value2>user123</Value2>
<Count>5</Count>
</Root>
</Roots>
I have been trying a lot to transform this xml and here is my code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Roots">
<Roots>
<Root>
<xsl:for-each-group select="Root" group-by=
"concat(Value1, '+', Value2)">
<xsl:copy-of select=
"current-group()[1]/*[starts-with(name(),'key')]"/>
<Count>
<xsl:value-of select="sum(current-group()/Count)"/>
</Count>
</xsl:for-each-group>
</Root>
</Roots>
</xsl:template>
</xsl:stylesheet>
I know I am doing wrong and could be lot easier to the expert but I really need some help and direction.
Thanks
Your XSLT was close, but move the <Root> element definition into the xsl:for-each-group. I couldn't make sense of your xsl:copy-of..., so I just reproduced the elements one-by-one. So use this simple XSLT-2.0 stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/Roots">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:for-each-group select="Root" group-by="Value1">
<Root>
<Value1><xsl:value-of select="current-grouping-key()" /></Value1>
<Value2><xsl:value-of select="current-group()[1]/Value2" /></Value2>
<Count><xsl:value-of select="sum(current-group()/Count)" /></Count>
</Root>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It's output is:
<Roots>
<Root>
<Value1>Links1</Value1>
<Value2>notadmin</Value2>
<Count>11</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>userxyz</Value2>
<Count>20</Count>
</Root>
<Root>
<Value1>Links3</Value1>
<Value2>user123</Value2>
<Count>5</Count>
</Root>
</Roots>
Right now, your grouping concat(Value1, '+', Value2) will not yield three nodes since nodes at Links2 maintains two different value2 node values. Also, your identity transform template is redundant since you rewrite the XML tree directly from root, Roots.
One Grouping (only Value1)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="Roots">
<Roots>
<xsl:for-each-group select="Root" group-by="Value1">
<Root>
<xsl:copy-of select="Value1|Value2"/>
<Count>
<xsl:value-of select="sum(current-group()/Count)" />
</Count>
</Root>
</xsl:for-each-group>
</Roots>
</xsl:template>
</xsl:stylesheet>
Returns with first value2 returned:
<Roots>
<Root>
<Value1>Links1</Value1>
<Value2>notadmin</Value2>
<Count>11</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>userxyz</Value2>
<Count>20</Count>
</Root>
<Root>
<Value1>Links3</Value1>
<Value2>user123</Value2>
<Count>5</Count>
</Root>
</Roots>
Two Groupings (Value1 and Value2)
Keeping original grouping:
<xsl:for-each-group select="Root" group-by="concat(Value1, '+', Value2)">
Returns four nodes
<Roots>
<Root>
<Value1>Links1</Value1>
<Value2>notadmin</Value2>
<Count>11</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>userxyz</Value2>
<Count>10</Count>
</Root>
<Root>
<Value1>Links2</Value1>
<Value2>usermnp</Value2>
<Count>10</Count>
</Root>
<Root>
<Value1>Links3</Value1>
<Value2>user123</Value2>
<Count>5</Count>
</Root>
</Roots>

Build nodes from xpath

I have a source xml and from this source xml I like to select the nodes given by a path e.g. /shiporder/item/title and /shiporder/shipto/name from the sample source:
<?xml version="1.0" encoding="utf-16"?>
<shiporder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" orderid="orderid1">
<orderperson>orderperson1</orderperson>
<shipto>
<name>name1</name>
</shipto>
<item>
<title>foo</title>
</item>
<item>
<title>bar</title>
</item>
</shiporder>
And I like to transform those nodes to certain target tree e.g. each /shiporder/item/title from the source xml should copied to root/Customer/Name/Title in the target xml tree. So my idea was to generate for each level in the source path a template and call this template from the preceding level:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:func="http://www.functx.com">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="/shiporder"/>
</root>
</xsl:template>
<xsl:template match="/shiporder">
<Customer>
<xsl:apply-templates select="item"/>
<xsl:apply-templates select="shipto"/>
</Customer>
</xsl:template>
<xsl:template match="/shiporder/item">
<Name>
<xsl:apply-templates select="title"/>
</Name>
</xsl:template>
<xsl:template match="/shiporder/shipto">
<Address>
<xsl:apply-templates select="name"/>
</Address>
</xsl:template>
<xsl:template match="/shiporder/item/title">
<Title>
<xsl:value-of select="text()" />
</Title>
</xsl:template>
<xsl:template match="/shiporder/shipto/name">
<Street>
<xsl:value-of select="text()" />
</Street>
</xsl:template>
</xsl:stylesheet>
There for I get a huge stylesheet if I have a huge list of source-paths. Has some one a more feasible idea to reach the target?

XSLT for picking the first detail corresponding to one field

I am writing xslt for filtering the data based on one of the fields.
Here is my input xml:
<?xml version="1.0" encoding="UTF-8"?>
<Consumer>
<header>
<msfnm>MSFNM</msfnm>
<mslnm>MSLNM</mslnm>
<msmnm>MSMNM</msmnm>
<msssn>MSSSN</msssn>
<csscstno>CSSCSTNO</csscstno>
<msact>MSACT</msact>
</header>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
<data>
<msfnm>Nitin1</msfnm>
<mslnm>Jain1</mslnm>
<msmnm>L1</msmnm>
<msssn>1233</msssn>
<csscstno>111</csscstno>
<msact>1233556</msact>
</data>
<data>
<msfnm>Nitin2</msfnm>
<mslnm>Jain2</mslnm>
<msmnm>L1</msmnm>
<msssn>1234</msssn>
<csscstno>123</csscstno>
<msact>12334256</msact>
</data>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
and I want my output xml should be like
<?xml version="1.0" encoding="UTF-8"?>
<Consumer>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
<data>
<msfnm>Nitin2</msfnm>
<mslnm>Jain2</mslnm>
<msmnm>L1</msmnm>
<msssn>1234</msssn>
<csscstno>123</csscstno>
<msact>12334256</msact>
</data>
</Consumer>
Condition: basically what I want, to take first occurrence of csscstno only. If in next occurrence, csscstno is same, then the whole set should be rejected.
My xslt:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xml>
<xsl:for-each select="/Consumer/data">
<Consumer>
<msfnm><xsl:value-of select="msfnm"/></msfnm>
<mslnm><xsl:value-of select="mslnm"/></mslnm>
<msmnm><xsl:value-of select="msmnm"/></msmnm>
<msssn><xsl:value-of select="msssn"/></msssn>
<xsl:if test="position()=1">
<csscstno><xsl:value-of select="csscstno"/></csscstno>
</xsl:if>
<msact><xsl:value-of select="msact"/></msact>
</Consumer>
</xsl:for-each>
</xml>
</xsl:template>
</xsl:stylesheet>
This is not working. Let me know, what I am doing wrong here.
You can use keys. Just like the stylesheet below:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" />
<!-- create a key for your target node -->
<xsl:key name="Customer_No" match="csscstno" use="."/>
<!-- copy only the 1st matched keys in the output -->
<xsl:template match="/">
<Consumer>
<xsl:copy-of select="Consumer/data[count(csscstno | key('Customer_No', csscstno)[1]) = 1]"/>
<!-- or you can use the following line instead
<xsl:copy-of select="Consumer/data[generate-id(csscstno) = generate-id(key('Customer_No', csscstno)[1])]"/>
-->
</Consumer>
</xsl:template>
</xsl:transform>
when this is applied to your input XML, the result is:
<?xml version="1.0" encoding="utf-8"?>
<Consumer>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
<data>
<msfnm>Nitin2</msfnm>
<mslnm>Jain2</mslnm>
<msmnm>L1</msmnm>
<msssn>1234</msssn>
<csscstno>123</csscstno>
<msact>12334256</msact>
</data>
</Consumer>
Or, you can use keys for grouping data by csscstno to get the first occurence. Also, You can use a copy-of to select "data":
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="dataByCsscstno" match = "data" use = "csscstno"/>
<xsl:template match="/">
<Consumer>
<xsl:copy-of select="/Consumer/data[generate-id() = generate-id(key('dataByCsscstno', csscstno)[1])]"/>
</Consumer>
</xsl:template>
</xsl:stylesheet>
Eventually I have found another way, which is working as expected.
Here is my xslt:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xml>
<xsl:for-each-group select="/Consumer/data" group-by="csscstno">
<xsl:copy-of select="current-group()[1]"/>
</xsl:for-each-group>
</xml>
</xsl:template>
</xsl:stylesheet>