Merge two xml files based on key field - xslt

I have requirement two merge two xml files. Based on the key field in the files I want to merge entire content of the xml 1 to xml 2.
Could you please help me to achieve this scenario.
I have tried this xslt but I am getting below error.
"a sequence of more than one item is not allowed as the first argument of fn:parse-xml()"
XSLT code.
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:mode on-no-match="shallow-copy" />
<xsl:param name="XML1" />
<xsl:variable name="details-doc" select="parse-xml($XML1)" />
<xsl:template match="Key">
<xsl:copy-of select="." />
<xsl:variable name="Key" select="string(.)" />
<xsl:copy-of select="$details-doc/Record/Detail[Key = $Key]" />
</xsl:template>
</xsl:stylesheet>
XML1.
<Record>
<Detail>
<Key>1</Key>
<Place>Ocean</Place>
<City>Urban</City>
</Detail>
<Detail>
<Key>2</Key>
<Place>Road</Place>
<City>Rural</City>
</Detail>
<Detail>
<Key>3</Key>
<Place>Plane</Place>
<City>Semiurban</City>
</Detail>
</Record>
XML2
<Record>
<Contact>
<Key>1</Key>
<Name>Jack</Name>
</Contact>
<Contact>
<Key>2</Key>
<Name>Ethan</Name>
</Contact>
<Contact>
<Key>3</Key>
<Name>Ron</Name>
</Contact>
</Record>
And the expected output.
<Record>
<Contact>
<Key>1</Key>
<Name>Jack</Name>
<Place>Ocean</Place>
<City>Urban</City>
</Contact>
<Contact>
<Key>2</Key>
<Name>Ethan</Name>
<Place>Road</Place>
<City>Rural</City>
</Contact>
<Contact>
<Key>3</Key>
<Name>Ron</Name>
<Place>Plane</Place>
<City>Semiurban</City>
</Contact>
</Record>

Related

XSLT 1.0 Help for Multi part Message (BizTalk)

Having a requirement where i have aggregated message and populate and loop for each message in output , below is what i have but need help where to start with.
Input Message :
<ns0:Root xmlns:ns0="http://schemas.microsoft.com/BizTalk/2003/aggschema">
<InputMessagePart_0>
<ns0:Root xmlns:ns0="http://TestXSLT1._0.Inpu1">
<Header>
<SeqNo>1</SeqNo>
<FileName>Test</FileName>
</Header>
<Detail>
<ItemName>Item1</ItemName>
<Quantity>1</Quantity>
</Detail>
<Detail>
<ItemName>Item2</ItemName>
<Quantity>2</Quantity>
</Detail>
</ns0:Root>
</InputMessagePart_0>
<InputMessagePart_1>
<ns0:Root xmlns:ns0="http://TestXSLT1._0.Input2">
<FileName>Test</FileName>
<Header>
<DestinationLocation>Miami</DestinationLocation>
<DestinationName>State</DestinationName>
<Detail>
<ItemName>Item1</ItemName>
<Rate>100</Rate>
</Detail>
<Detail>
<ItemName>Item2</ItemName>
<Rate>200</Rate>
</Detail>
</Header>
</ns0:Root>
</InputMessagePart_1>
</ns0:Root>
Desired OutPut :
<ns0:Root xmlns:ns0="http://TestXSLT1._0.Output">
<SeqNo>1</SeqNo>
<FileName>Test</FileName>
<DestinationLocation>Miami</DestinationLocation>
<DestinationName>State</DestinationName>
<Detail>
<ItemName>Item1</ItemName>
<Quantity>1</Quantity>
</Detail>
<Detail>
<ItemName>Item2</ItemName>
<Quantity>2</Quantity>
</Detail>
<Detail>
<ItemName>Item1</ItemName>
<Rate>100</Rate>
</Detail>
<Detail>
<ItemName>Item2</ItemName>
<Rate>200</Rate>
</Detail>
</ns0:Root>
XSLT I Have started :
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s2 s0 s1" version="1.0" xmlns:ns0="http://TestXSLT1._0.Output" xmlns:s2="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:s0="http://TestXSLT1._0.Input2" xmlns:s1="http://TestXSLT1._0.Inpu1">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s2:Root" />
</xsl:template>
<xsl:template match="/s2:Root">
<ns0:Root>
<SeqNo>
<xsl:value-of select="InputMessagePart_0/s1:Root/Header/SeqNo/text()" />
</SeqNo>
<FileName>
<xsl:value-of select="InputMessagePart_0/s1:Root/Header/FileName/text()" />
</FileName>
<DestinationLocation>
<xsl:value-of select="InputMessagePart_1/s0:Root/Header/DestinationLocation/text()" />
</DestinationLocation>
<DestinationName>
<xsl:value-of select="InputMessagePart_1/s0:Root/Header/DestinationName/text()" />
</DestinationName>
<xsl:for-each select="InputMessagePart_0/s1:Root/Detail">
<Detail>
<ItemName>
<xsl:value-of select="ItemName/text()" />
</ItemName>
<Quantity>
<xsl:value-of select="Quantity/text()" />
</Quantity>
<Rate>
<xsl:value-of select="../../../InputMessagePart_1/s0:Root/Header/Detail/Rate/text()" />
</Rate>
</Detail>
</xsl:for-each>
</ns0:Root>
</xsl:template>
</xsl:stylesheet>
The XSLT I have made doesn't give me the desired output.
This should be done in XSLT 1.0
Appreciate for the help ~
The output you show can be produced using:
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" omit-xml-declaration="yes"/>
<xsl:template match="/*">
<ns0:Root xmlns:ns0="http://TestXSLT1._0.Output">
<!-- header -->
<SeqNo>
<xsl:value-of select="InputMessagePart_0/*/Header/SeqNo" />
</SeqNo>
<FileName>
<xsl:value-of select="InputMessagePart_0/*/Header/FileName" />
</FileName>
<DestinationLocation>
<xsl:value-of select="InputMessagePart_1/*/Header/DestinationLocation" />
</DestinationLocation>
<DestinationName>
<xsl:value-of select="InputMessagePart_1/*/Header/DestinationName" />
</DestinationName>
<!-- details part 0 -->
<xsl:for-each select="InputMessagePart_0/*/Detail">
<Detail>
<ItemName>
<xsl:value-of select="ItemName" />
</ItemName>
<Quantity>
<xsl:value-of select="Quantity" />
</Quantity>
</Detail>
</xsl:for-each>
<!-- details part 1 -->
<xsl:for-each select="InputMessagePart_1/*/Header/Detail">
<Detail>
<ItemName>
<xsl:value-of select="ItemName" />
</ItemName>
<Rate>
<xsl:value-of select="Rate" />
</Rate>
</Detail>
</xsl:for-each>
</ns0:Root>
</xsl:template>
</xsl:stylesheet>

Read xml file to capture the values from <value> tags

We have a requirement to read the xml file and capture the EmployeeName and EmailId values from tags to create the output as xml file.
The first tag always represents EmployeeName and 5th tag always represents EmailId.
Need to capture the values present in the row/value....
The input xml file as follows:
<?xml version="1.0" encoding="utf-8"?>
<dataset xmlns="http://developer.net.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<!--
<dataset
xmlns="http://developer.net.com/schemas/xmldata/1/"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://developer.net.com/schemas/xmldata/1/ xmldata.xsd"
>
-->
<metadata>
<item length="20" type="xs:string" name="EmployeeName"/>
<item length="4" type="xs:string" name="Full/Part Time Code"/>
<item type="xs:dateTime" name="Hire Date"/>
<item type="xs:dateTime" name="Termination Date"/>
<item length="30" type="xs:string" name="EmailID"/>
<item length="30" type="xs:string" name="State"/>
</metadata>
<data>
<row>
<value>JOSEPH</value>
<value>F</value>
<value>1979-04-19T00:00:00</value>
<value>2007-08-27T00:00:00</value>
<value>joseph.Tim#gmail.com</value>
<value>TX</value>
</row>
<row>
<value>NANDY</value>
<value>F</value>
<value>1979-04-19T00:00:00</value>
<value>2007-08-27T00:00:00</value>
<value>Nandy123#gmailcom</value>
<value>PA</value>
</row>
</data>
</dataset>
The Expected Ouput as below:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:EMPLOYEEDETAILS xmlns:ns0="http://net.com/EmployeeDetails">
<Records>
<EmployeeName>JOSEPH</EmployeeName>
<EmailId>joseph.Tim#gmail.com</EmailId>
</Records>
<Records>
<EmployeeName>NANDY</EmployeeName>
<EmailId>Nandy123#gmailcom</EmailId>
</Records>
</ns0:EMPLOYEEDETAILS>
Thanks,
Ravi
Please try the XSLT below. You need to make additional changes for matching namespaces according to the input XML and adding the root node <EMPLOYEEDETAILS> in the output.
EDIT: XSLT solution updated to handle the namespace issue. Root node <ns0:EMPLOYEEDETAILS> included in the solution.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://developer.net.com/schemas/xmldata/1/">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:param name="param-name" select="1" />
<xsl:param name="param-email" select="5" />
<xsl:template match="/">
<ns0:EMPLOYEEDETAILS>
<xsl:for-each select="//ns0:data/ns0:row">
<Records>
<xsl:for-each select="ns0:value">
<xsl:choose>
<xsl:when test="position() = $param-name">
<EmployeeName>
<xsl:value-of select="." />
</EmployeeName>
</xsl:when>
<xsl:when test="position() = $param-email">
<EmailId>
<xsl:value-of select="." />
</EmailId>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</Records>
</xsl:for-each>
</ns0:EMPLOYEEDETAILS>
</xsl:template>
</xsl:stylesheet>
Output
<ns0:EMPLOYEEDETAILS xmlns:ns0="http://developer.net.com/schemas/xmldata/1/">
<Records>
<EmployeeName>JOSEPH</EmployeeName>
<EmailId>joseph.Tim#gmail.com</EmailId>
</Records>
<Records>
<EmployeeName>NANDY</EmployeeName>
<EmailId>Nandy123#gmailcom</EmailId>
</Records>
</ns0:EMPLOYEEDETAILS>

XSLT: How to generate unique id for node based on value only

I have a source XML that contains address elements which could have the same values (please note that Contact/id=1 and Contact/id=3 have the same address:
<?xml version="1.0" encoding="utf-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Contact>
<id>1</id>
<Address>
<City City="Wien" />
<Postcode Postcode="LSP-123" />
</Address>
</Contact>
<Contact>
<id>2</id>
<Address>
<City City="Toronto" />
<Postcode Postcode="LKT-947" />
</Address>
</Contact>
<Contact>
<id>3</id>
<Address>
<City City="Wien" />
<Postcode Postcode="LSP-123" />
</Address>
</Contact>
</Contacts>
Desired output with XSLT 1.0:
<?xml version="1.0" encoding="utf-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Contact>
<id>1</id>
<Address>SomeId_1</Address>
</Contact>
<Contact>
<id>2</id>
<Address>SomeId_2</Address>
</Contact>
<Contact>
<id>3</id>
<Address>SomeId_1</Address>
</Contact>
</Contacts>
When I used function generate-id(Address) I got different id for addresses in Contact 1 and Contact 3. What other way to generate unique id for node based on its value only?
Thank you for the help.
I would advise building a key of values as a lookup table and then just orienting from the first entry of the lookup table for the unique number:
t:\ftemp>type ivan.xml
<?xml version="1.0" encoding="utf-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Contact>
<id>1</id>
<Address>
<City City="Wien" />
<Postcode Postcode="LSP-123" />
</Address>
</Contact>
<Contact>
<id>2</id>
<Address>
<City City="Toronto" />
<Postcode Postcode="LKT-947" />
</Address>
</Contact>
<Contact>
<id>3</id>
<Address>
<City City="Wien" />
<Postcode Postcode="LSP-123" />
</Address>
</Contact>
</Contacts>
t:\ftemp>call xslt ivan.xml ivan.xsl
<?xml version="1.0" encoding="utf-8"?><Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Contact>
<id>1</id>
<Address>SomeId_1</Address>
</Contact>
<Contact>
<id>2</id>
<Address>SomeId_2</Address>
</Contact>
<Contact>
<id>3</id>
<Address>SomeId_1</Address>
</Contact>
</Contacts>
t:\ftemp>type ivan.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="city-pc-pair" match="Address"
use="concat(City/#City,'
',Postcode/#PostCode)"/>
<xsl:template match="Address">
<xsl:for-each select="key('city-pc-pair',
concat(City/#City,'
',Postcode/#PostCode))[1]">
<Address>SomeId_<xsl:number level="any"/></Address>
</xsl:for-each>
</xsl:template>
<xsl:template match="#*|node()"><!--identity for all other nodes-->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
t:\ftemp>rem Done!
As for the concatenation that I'm using, I tell my students the technique of using a carriage return as a field delimiter reduces the likelihood of an unintended value collision to an infinitesimal size since there are very few hard carriage returns in XML content (those carriage returns that are parts of end-of-line sequences are normalized to a line-feed and so do not appear in the data).
Edited to add the following entity technique that may improve maintenance since it focuses the lookup expression to a single declaration in the stylesheet, so as not to be accidentally written differently in two different parts of the stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet
[
<!ENTITY lookup "concat(City/#City,'
',Postcode/#PostCode)">
]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="city-pc-pair" match="Address" use="&lookup;"/>
<xsl:template match="Address">
<xsl:for-each select="key('city-pc-pair',&lookup;)[1]">
<Address>SomeId_<xsl:number level="any"/></Address>
</xsl:for-each>
</xsl:template>
<xsl:template match="#*|node()"><!--identity for all other nodes-->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XSLT's generate-id() is intended for generating #xml:id's, which are generally attributes meant to uniquely identify a node in the document. So everytime you call generate-id(), you
should be getting a unique value.
The identifier's you want to generate are just data, and have nothing to do with what generate-id() does.
If you want an identifier whose value is based on the value of some other data, then you
should just generate it from that data. Concat those values together, for example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="Address">
<Address>
<xsl:value-of select="concat(City/#City, '+', Postcode/#Postcode)"/>
</Address>
</xsl:template>
Will produce:
<?xml version="1.0" encoding="UTF-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Contact>
<id>1</id>
<Address>Wien+LSP-123</Address>
</Contact>
<Contact>
<id>2</id>
<Address>Toronto+LKT-947</Address>
</Contact>
<Contact>
<id>3</id>
<Address>Wien+LSP-123</Address>
</Contact>
</Contacts>
If you have some other requirements for the identifier, then you can write a function
or use a lookup table to map from those keys to some other identifiers.

XSL Transformation to split a single node into two nodes in the same level

I have a source XML
<Cars>
<Car>
<Make>Fiat</Make>
<Colors>
<Color>RED</Color>
<Color>BLUE</Color>
</Colors>
</Car>
<Car>
<Make>Volvo</Make>
<Colors>
<Color>RED</Color>
<Color>WHITE</Color>
</Colors>
</Car>
<Car>
<Make>Renault</Make>
<Colors>
<Color>BLUE</Color>
<Color>BLACK</Color>
</Colors>
</Car>
</Cars>
which I want to transform into something like
<Cars>
<Detail>
<Name>MakeName</Name>
<Entry>Fiat</Entry>
<Entry>Volvo</Entry>
<Entry>Renault</Entry>
</Detail>
<Detail>
<Name>AvailableColors</Name>
<Entry>RED</Entry>
<Entry>BLUE</Entry>
<Entry>WHITE</Entry>
<Entry>BLACK</Entry>
</Detail>
<Cars>
I am new to XSL, and created one to do half of processing, but I am stuck with the getting of colors as separate elements in target
<xsl:template match="/">
<Cars>
<xsl:apply-templates />
</Cars>
</xsl:template>
<xsl:template match="Cars">
<xsl:apply-templates select="Car" />
</xsl:template>
<xsl:template match="Car">
<Detail>
<Name>MakeName</Name>
<xsl:apply-templates select="Make" />
</Detail>
</xsl:template>
<xsl:template match="Make">
<Entry><xsl:value-of select"text()"/></Entry>
</xsl:template>
I am not able to create XSL for <Name>AvailableColors</Name>, I am quite new to XSL and any help is greatly appreciated
Here is an XSLT 1.0 stylesheet that shows how to eliminate duplicate colors using Muenchian grouping:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="k1" match="Car/Colors/Color" use="."/>
<xsl:template match="Cars">
<xsl:copy>
<Detail>
<Name>MakeName</Name>
<xsl:apply-templates select="Car/Make"/>
</Detail>
<Detail>
<Name>AvailableColors</Name>
<xsl:apply-templates select="Car/Colors/Color[generate-id() = generate-id(key('k1', .)[1])]"/>
</Detail>
</xsl:copy>
</xsl:template>
<xsl:template match="Car/Make | Colors/Color">
<Entry>
<xsl:value-of select="."/>
</Entry>
</xsl:template>
</xsl:stylesheet>
See a generic "shredding" solution given in this answer:
https://stackoverflow.com/a/8597577/36305

Merging pairs of nodes based on attribute, new to template matching

Say I have the following XML:
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1"/>
<references tokID="t2"/>
</group>
<group gID="s2">
<references tokID="t3"/>
</group>
</relatedStuff>
</root>
Now, considering that a for-each loop for every token would be pretty inefficient and a bad idea, how would one go about using template matching, to transform this xml into the following?
<s id="everything_merged">
<tok id="t1" gID="s1" >blah</tok>
<tok id="t2" gID="s1" >blabla</tok>
<tok id="t3" gID="s2" >shovel</tok>
</s>
All I want from <s> is the "gID", the gID corresponding to the token in the <tokens>.
<xsl:for-each select="b:root/a:tokens/a:token">
<!-- and here some template matching -->
<xsl:attribute name="gID">
<xsl:value-of select="--correspondingNode's--#gID"/>
</xsl:attribute>
</xsl:for-each>
I'm pretty fuzzy on this sort of thing, so thank you very much for any help!
The following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<s id="everything_merged">
<xsl:apply-templates select="/root/tokens/token" />
</s>
</xsl:template>
<xsl:template match="token">
<tok id="{#ID}" gID="{/root/relatedStuff/group[
references[#tokID=current()/#ID]]/#gID}">
<xsl:apply-templates />
</tok>
</xsl:template>
</xsl:stylesheet>
Applied to this input (corrected for well-formedness):
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1" />
<references tokID="t2" />
</group>
<group gID="s2">
<references tokID="t3" />
</group>
</relatedStuff>
</root>
Produces:
<s id="everything_merged">
<tok id="t1" gID="s1">blah</tok>
<tok id="t2" gID="s1">blabla</tok>
<tok id="t3" gID="s2">shovel</tok>
</s>
A solution using keys and pure "push-style:
<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:key name="kgIDfromTokId" match="#gID"
use="../*/#tokID"/>
<xsl:template match="tokens">
<s id="everything_merged">
<xsl:apply-templates/>
</s>
</xsl:template>
<xsl:template match="token">
<tok id="{#ID}" gID="{key('kgIDfromTokId', #ID)}">
<xsl:apply-templates/>
</tok>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1" />
<references tokID="t2" />
</group>
<group gID="s2">
<references tokID="t3" />
</group>
</relatedStuff>
</root>
the wanted, correct result is produced:
<s id="everything_merged">
<tok id="t1" gID="s1">blah</tok>
<tok id="t2" gID="s1">blabla</tok>
<tok id="t3" gID="s2">shovel</tok>
</s>