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>
Related
I have the below XML data as input to my XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<Application>
<Data>
<Data1>
<name>Michale</name>
<age>65</age>
<Info>
<Alias name="M">
<Contactmail>abc#gmail.com</Contactmail>
<ContactPh>8988900009</ContactPh>
</Alias>
<Alias name="Q">
<Contactmail>abc#gmail.com</Contactmail>
<ContactPh>8988900009</ContactPh>
</Alias>
</Info>
</Data1>
<Data1>
<name>Albert</name>
<age>69</age>
<Info>
<Alias name="A">
<Contactmail>xyz#gmail.com</Contactmail>
<ContactPh>89889908709</ContactPh>
</Alias>
<Alias name="P">
<Contactmail>pqr#gmail.com</Contactmail>
<ContactPh>8988988779</ContactPh>
</Alias>
</Info>
</Data1>
</Data>
</Application>
And I want to pass the Data1 block whose Alias name matches with "M", i.e.:
<Application>
<Data>
<Data1>
<name>Michale</name>
<age>65</age>
<Info>
<Alias name=M>
<Contactmail>abc#gmail.com</Contactmail>
<ContactPh>8988900009</ContactPh>
</Alias>
<Alias name=Q>
<Contactmail>abc#gmail.com</Contactmail>
<ContactPh>8988900009</ContactPh>
</Alias>
</Info>
</Data1>
</Data>
</Application>
I am stuck as to how to access an loop(ie Alias) inside a test condition?
Is there any better way to do this xslt?
<xsl:for-each select="./*[local-name() = 'Application']/*[local-name() = 'Data']">
<xsl:if test="">
....
</xsl:if>
</xsl:for-each>
The following template will do the job. The explanations are in the code.
<?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:strip-space elements="*" /> <!-- Removes unnecessary space between elements -->
<!-- identity template --> <!-- Copies all nodes not matched by other templates -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Data1[Info/Alias/#name != 'M']" /> <!-- Ignores all Data1 elements which don't have an #name='M' attribute child -->
<xsl:template match="Data1[Info/Alias/#name = 'M']"> <!-- Matches all Data1 elements which have the desired child attribute -->
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Its output is:
<?xml version="1.0"?>
<Application>
<Data>
<Data1>
<name>Michale</name>
<age>65</age>
<Info>
<Alias name="M">
<Contactmail>abc#gmail.com</Contactmail>
<ContactPh>8988900009</ContactPh>
</Alias>
<Alias name="Q">
<Contactmail>abc#gmail.com</Contactmail>
<ContactPh>8988900009</ContactPh>
</Alias>
</Info>
</Data1>
</Data>
</Application>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Application">
<xsl:copy>
<xsl:for-each select="Data/Data1">
<xsl:if test="Info/Alias[#name='M']">
<Data>
<Data1>
<xsl:apply-templates/>
</Data1>
</Data>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
You may also do like this
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>
For each <LineDetail> node that has the same value of <CtryCd> and populates 1 <LineDetail> with the summation of <Amt>.
But, I need to check also the <SI> and <TI> node. The output tag of the summation will be based from the <SI> and <TI>
node. The <Amt1> will generate if the value of <SI> and <TI> are both false, while <Amt2> will generate if the value of <SI> is true and <TI> is false.
I created an XSLT and already got the summation. But there's still something missing in my output.
This is a sample XML:
<Record>
<Data>
<Process>
<Header>
<ID>22-BBB</ID>
<Date>2017-02-14</Date>
<ContactName>Abegail</ContactName>
<!-- some other elements -->
</Header>
<Detail>
<ID>22-CCC</ID>
<RequestedDate>2017-02-14</RequestedDate>
<!-- some other elements -->
<LineDetail>
<CtryCd>AF</CtryCd>
<SI>false</SI>
<TI>false</TI>
<Amt>11.11</Amt>
</LineDetail>
<LineDetail>
<CtryCd>SE</CtryCd>
<SI>true</SI>
<TI>false</TI>
<Amt>22.22</Amt>
</LineDetail>
<LineDetail>
<CtryCd>AF</CtryCd>
<SI>false</SI>
<TI>false</TI>
<Amt>33.33</Amt>
</LineDetail>
<LineDetail>
<CtryCd>AF</CtryCd>
<SI>true</SI>
<TI>false</TI>
<Amt>55.55</Amt>
</LineDetail>
</Detail>
</Process>
</Data>
</Record>
Generated output:
<Record>
<Data>
<Process>
<Header>
<ID>22-BBB</ID>
<Date>2017-02-14</Date>
<ContactName>Abegail</ContactName>
<!-- some other elements -->
</Header>
<Detail>
<LineDetail>
<CtryCd>AF</CtryCd>
<Amt1>99.99</Amt1>
</LineDetail>
</Detail>
<Detail>
<LineDetail>
<C<Amt2>22.22</Amt2>
</LineDetail>
</Detail>
</Process>
</Data>
</Record>
Expected output:
<Record>
<Data>
<Process>
<Header>
<ID>22-BBB</ID>
<Date>2017-02-14</Date>
<ContactName>Abegail</ContactName>
<!-- some other elements -->
</Header>
<Detail>
<ID>22-CCC</ID>
<RequestedDate>2017-02-14</RequestedDate>
<!-- some other elements -->
<LineDetail>
<CtryCd>AF</CtryCd>
<Amt1>44.44</<Amt1>
<Amt2>55.55</Amt2>
</LineDetail>
<LineDetail>
<CtryCd>SE</CtryCd>
<Amt1>0</<Amt1>
<Amt2>22.22</Amt2>
</LineDetail>
</Detail>
</Process>
</Data>
</Record>
XSLT:
<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="*"/>
<xsl:key name="CtryCd" match="LineDetail" use="CtryCd"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail">
<xsl:for-each-group select="LineDetail" group-by="CtryCd">
<Detail>
<LineDetail>
<CtryCd>
<xsl:value-of select="CtryCd"/>
</CtryCd>
<xsl:if test="lower-case(SI)='false' and lower-case(TI)='false'">
<Amt1>
<xsl:value-of select="sum(current-group()/Amt)"/>
</Amt1>
<xsl:if test="lower-case(SI)='true' and lower-case(TI)='false'">
<Amt2>
<xsl:value-of select="sum(current-group()/Amt)"/>
</Amt2>
</xsl:if>
</xsl:if>
<xsl:if test="lower-case(SI)='true' and lower-case(TI)='false'">
<Amt2>
<xsl:value-of select="sum(current-group()/Amt)"/>
</Amt2>
</xsl:if>
</LineDetail>
</Detail>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
You current if condition will only check the first item in the group. You really need it to be part of the actual sum statement, as a condition on each node on the group.
<Amt1>
<xsl:value-of select="sum(current-group()[lower-case(SI)='false' and lower-case(TI)='false']/Amt)"/>
</Amt1>
You would then have a similar statement for the Amt2, testing for "true" and "false"
Try this XSLT
<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="*"/>
<xsl:key name="CtryCd" match="LineDetail" use="CtryCd"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail">
<xsl:for-each-group select="LineDetail" group-by="CtryCd">
<Detail>
<LineDetail>
<CtryCd>
<xsl:value-of select="CtryCd"/>
</CtryCd>
<Amt1>
<xsl:value-of select="sum(current-group()[lower-case(SI)='false' and lower-case(TI)='false']/Amt)"/>
</Amt1>
<Amt2>
<xsl:value-of select="sum(current-group()[lower-case(SI)='true' and lower-case(TI)='false']/Amt)"/>
</Amt2>
</LineDetail>
</Detail>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
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
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>