I have a requirement where there is a XML structure with a root element having 2 child element of array type and
Sample request structure like below
<Root>
<Header>
<Inv>12</Inv>
</Header>
<Detail>
<Val>aa</Val>
<Line>1</Line>
</Detail>
<Header>
<Inv>15</Inv>
</Header>
<Detail>
<Val>bb</Val>
<Line>2</Line>
</Detail>
</Root>
I have to get response like below:
<CreateInvoice>
<Data>
<Invoice>
<Inv>12</Inv>
<InvoiceLine>
<Val>aa</Val>
<Line>1</Line>
</InvoiceLine>
</Invoice>
</Data>
<Data>
<Invoice>
<Inv>15</Inv>
<InvoiceLine>
<Val>bb</Val>
<Line>2</Line>
</InvoiceLine>
</Invoice>
</Data>
</CreateInvoice>
I tried using nested for-each on Data , but not able to get the response.
Either only inv is populating or InvoiceLine is populating.
If each invoice has exactly one Header and one Detail then you can do simply:
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:template match="/Root">
<CreateInvoice>
<xsl:for-each select="Header">
<Data>
<Invoice>
<xsl:copy-of select="Inv"/>
<InvoiceLine>
<xsl:copy-of select="following-sibling::Detail[1]/*"/>
</InvoiceLine>
</Invoice>
</Data>
</xsl:for-each>
</CreateInvoice>
</xsl:template>
</xsl:stylesheet>
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>
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORDS>
<Group>
<Name>12345</Name>
<Grp>MANAGER</Grp>
<FName>Alex</FName>
<LName>Johnson</LName>
<String1>abcd</String1>
/Group>
<Group>
<Name>67891</Name>
<Grp>PROJECT MANAGER</Grp>
<FName>JAMES</FName>
<LName>HARPER</LName>
<String1></String1>
</Group> </RECORDS> <LOGIN>
<User>
<Name>12345</UserName>
<Last>14/02/2013</Last>
</User>
<User>
<Name>67891</Name>
<Last>14/01/2013/Last>
</User> </LOGIN> </DATA>
Requirement:
In output XML
If String1 has a value then Type tag should have value as "axbx" and
if String1 is blank then Type tag should have value as "dydy"
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORDS>
<Group>
<Name>12345</Name>
<Grp>MANAGER</Grp>
<FName>Alex</FName>
<LName>Johnson</LName>
<Type>axbx</Type>
</Group>
<Group>
<Name>67891</Name>
<Grp>PROJECT MANAGER</Grp>
<FName>JAMES</FName>
<LName>HARPER</LName>
<Type>dydy</Type>
</Group> </RECORDS> </DATA>
Please suggest.
I can't edit your question so I copy the corrected XML:
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORDS>
<Group>
<Name>12345</Name>
<Grp>MANAGER</Grp>
<FName>Alex</FName>
<LName>Johnson</LName>
<String1>abcd</String1>
</Group>
<Group>
<Name>67891</Name>
<Grp>PROJECT MANAGER</Grp>
<FName>JAMES</FName>
<LName>HARPER</LName>
<String1></String1>
</Group>
</RECORDS>
<LOGIN>
<User>
<Name>12345</Name>
<Last>14/02/2013</Last>
</User>
<User>
<Name>67891</Name>
<Last>14/01/2013</Last>
</User>
</LOGIN>
</DATA>
and the XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="String1">
<Type>
<xsl:choose>
<xsl:when test="string-length(.) > 0">axbx</xsl:when>
<xsl:otherwise>dydy</xsl:otherwise>
</xsl:choose>
</Type>
</xsl:template>
</xsl:stylesheet>
I'm not very experienced so there might be a better way.
I am working in a project where I need to calculate the sum of hours after blank hours until the next blank hours and display them as in the output.
Here is the Input:
<Nodes>
<Node>
<EmpId>1<EmpId>
<InTime></InTime>
<Hours></Hours>
</Node>
<Node>
<EmpId>1<EmpId>
<InTime>10/12/2010</InTime>
<Hours>5</Hours>
</Node>
<Node>
<EmpId>1<EmpId>
<InTime>10/13/2010</InTime>
<Hours>5</Hours>
</Node>
<Node>
<EmpId>1<EmpId>
<InTime></InTime>
<Hours></Hours>
</Node>
<Node>
<EmpId>1</EmpId>
<InTime></InTime>
<Hours></Hours>
</Node>
<Node>
<EmpId>1</EmpId>
<InTime>10/14/2010</InTime>
<Hours>2</Hours>
</Node>
<Node>
<EmpId>1</EmpId>
<InTime>10/14/2010</InTime>
<Hours>3</Hours>
</Node>
</Nodes>
Output should be like:
<Nodes>
<Detail>
<EmpId>1</EmpId>
<InTime>10/12/2010</InTime>
<Hours>10</Hours>
</Detail>
<Detail>
<EmpId>1</EmpId>
<InTime>10/14/2010</InTime>
<Hours>5</Hours>
</Detail>
</Nodes>
Appreciate if any one could help me on this.
Your input XML is malformed (several <EmpId> tags where you should have </EmpId>), but once that's fixed, I believe this does what you describe:
<?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"/>
<xsl:template match="/Nodes">
<Nodes>
<xsl:apply-templates select="Node[Hours != '' and not(normalize-space(preceding-sibling::Node[1]/Hours))]" />
</Nodes>
</xsl:template>
<xsl:template match="Node">
<Detail>
<xsl:copy-of select="EmpId | InTime"/>
<Hours>
<xsl:apply-templates select="." mode="SumHours" />
</Hours>
</Detail>
</xsl:template>
<xsl:template match="Node[normalize-space(following-sibling::Node[1]/Hours)]" mode="SumHours">
<xsl:param name="total" select="0" />
<xsl:apply-templates select="following-sibling::Node[1]" mode="SumHours">
<xsl:with-param name="total" select="$total + Hours" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Node" mode="SumHours">
<xsl:param name="total" select="0" />
<xsl:value-of select="$total + Hours"/>
</xsl:template>
</xsl:stylesheet>
I have been working with XSLT lately, but now I got to a new problem trying to group a new xml file. I can't work out how to put a scope on the grouping. I want to group the <Detail> inside every <Info> node. Simple sample file:
<?xml version="1.0" encoding="utf-8"?>
<File>
<Info>
<Id>1111</Id>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
<Detail type="A" group="1">
<Data>
<Nr>6</Nr>
</Data>
</Detail>
</Info>
<Info>
<Id>2222</Id>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="A" group="1">
<Data>
<Nr>3</Nr>
</Data>
</Detail>
</Info>
</File>
The output should be
<?xml version="1.0" encoding="utf-8"?>
<File>
<Info>
<Id>1111</Id>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
<Data>
<Nr>6</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
</Info>
<Info>
<Id>2222</Id>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>3</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>2</Nr>
</Data>
</Detail>
</Info>
</File>
In my try I don't know how to copy the values from the <Info> element (ID, it could be other elements too), I just write out <Info> element, and every <Detail> gets grouped in the first <Info> element, leaving the last <Info> element empty.
Here is my xslt so far
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="details" match="Detail"
use="concat(#type,'_',#group)"/>
<xsl:template match='/'>
<File>
<xsl:for-each select="File/Info">
<Info>
<xsl:for-each select="Detail[count(. | key('details', concat(#type,'_',#group))[1]) = 1]">
<xsl:sort select="concat(#type,'_',#group)" />
<Detail type="{#type}" group="{#group}">
<xsl:for-each select="key('details', concat(#type,'_',#group))">
<xsl:copy-of select="Data"/>
</xsl:for-each>
</Detail>
</xsl:for-each>
</Info>
</xsl:for-each>
</File>
</xsl:template>
</xsl:stylesheet>
and here is my result so far
<File>
<Info>
<Detail type="A" group="1">
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
<Data>
<Nr>6</Nr>
</Data>
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>3</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
</Info>
<Info />
</File>
Thanks for any help :)
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:key name="kDetailChildren" match="Detail"
use="concat(generate-id(..),'+',#type,'+',#group)"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"Detail
[not(generate-id()
=
generate-id(key('kDetailChildren',
concat(generate-id(..),'+',#type,'+',#group)
)[1])
)]"/>
<xsl:template match="Detail">
<Detail>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select=
"key('kDetailChildren',
concat(generate-id(..),'+',#type,'+',#group)
)/node()"/>
</Detail>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<File>
<Info>
<Id>1111</Id>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
<Detail type="A" group="1">
<Data>
<Nr>6</Nr>
</Data>
</Detail>
</Info>
<Info>
<Id>2222</Id>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="A" group="1">
<Data>
<Nr>3</Nr>
</Data>
</Detail>
</Info>
</File>
produces the wanted, correct result:
<File>
<Info>
<Id>1111</Id>
<Detail type="A" group="1">
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
<Data>
<Nr>6</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
</Info>
<Info>
<Id>2222</Id>
<Detail type="A" group="1">
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>3</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>2</Nr>
</Data>
</Detail>
</Info>
</File>
Explanation:
Proper use of the identity rule and the Muenchian grouping method using composite keys.
Note how the parent's identity is included in the key.