XSLT grouping multiple levels [duplicate] - xslt

I'm trying to group the input below by the destination and assortment values using muenchian-grouping which is new for me so I'm not sure how to do it properly. The input files will be much larger than this so performance is important.
<?xml version="1.0"?>
<ns0:Data xmlns:ns0="http://BizTalk_Projects.input">
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA961</assortment>
<quantity>10</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA961</assortment>
<quantity>15</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA969</assortment>
<quantity>15</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA972</assortment>
<quantity>5</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_SA980</assortment>
<quantity>20</quantity>
</transports>
<transports>
<destination>destination 2</destination>
<assortment>Volvo_GA960</assortment>
<quantity>10</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Nissan_GA963</assortment>
<quantity>5</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Nissan_GA963</assortment>
<quantity>5</quantity>
</transports>
</ns0:Data>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Destinations xmlns:ns0="http://BizTalk_Projects.output">
<Destination>
<name>destination 1</name>
<assortment>
<name>Volvo_GA</name>
<row>
<type>sumPerAssortment</type>
<id>961</id>
<totalQuantity>25</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerAssortment</type>
<id>969</id>
<totalQuantity>15</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerAssortment</type>
<id>972</id>
<totalQuantity>5</totalQuantity>
<region>2</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>40</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>5</totalQuantity>
<region>2</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>45</totalQuantity>
<region />
</row>
</assortment>
<assortment>
<name>Volvo_SA</name>
<row>
<type>sumPerAssortment</type>
<id>980</id>
<totalQuantity>20</totalQuantity>
<region>3</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>20</totalQuantity>
<region>3</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>20</totalQuantity>
<region />
</row>
</assortment>
<assortment>
<name>Nissan_GA</name>
<row>
<type>sumPerAssortment</type>
<id>963</id>
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>10</totalQuantity>
<region />
</row>
</assortment>
</Destination>
<Destination>
<name>destination 2</name>
<assortment>
<name>Volvo_GA</name>
<row>
<type>sumPerAssortment</type>
<id>960</id>
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>10</totalQuantity>
<region />
</row>
</assortment>
</Destination>
</ns0:Destinations>
Note:
assortment number starting with 96 = region 1
assortment number starting with 97 = region 2
assortment number starting with 98 = region 3
Start of my XSLT:
<?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 s0"
version="1.0"
xmlns:s0="http://BizTalk_Projects.input"
xmlns:ns0="http://BizTalk_Projects.output">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="destinationKey" match="transports" use="destination"/>
<xsl:template match="/">
<xsl:apply-templates select="/s0:Data" />
</xsl:template>
<xsl:template match="/s0:Data">
<ns0:Destinations>
<xsl:for-each select="transports[count(. | key('destinationKey',destination)[1]) = 1]">
<Destination>
<name>
<xsl:value-of select="destination/text()" />
</name>
<xsl:for-each select="key('destinationKey',destination)">
<assortment>
<name>
<xsl:value-of select="substring(assortment/text(),1,string-length(assortment)-3)" />
</name>
</assortment>
</xsl:for-each>
</Destination>
</xsl:for-each>
</ns0:Destinations>
</xsl:template>
</xsl:stylesheet>
With this code, I'm getting this output (duplicate rows, but correct assortments for each destination);
<ns0:Destinations xmlns:ns0="http://BizTalk_Projects.output">
<Destination>
<name>destination 1</name>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_SA</name>
</assortment>
<assortment>
<name>Nissan_GA</name>
</assortment>
<assortment>
<name>Nissan_GA</name>
</assortment>
</Destination>
<Destination>
<name>destination 2</name>
<assortment>
<name>Volvo_GA</name>
</assortment>
</Destination>
</ns0:Destinations>
Any suggestions on how I can solve this? Help is very appreciated!

It's difficult to see how exactly the output relates to the input. Try this as your starting point:
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="transports-by-destination" match="transports" use="destination" />
<xsl:key name="transports-by-assortment" match="transports" use="concat(destination, '|', assortment)" />
<xsl:template match="/*">
<xsl:copy>
<!-- for each unique destination -->
<xsl:for-each select="transports[count(. | key('transports-by-destination', destination)[1]) = 1]">
<Destination>
<name>
<xsl:value-of select="destination"/>
</name>
<xsl:variable name="group" select="key('transports-by-destination', destination)" />
<!-- for each unique assortment in this destination -->
<xsl:for-each select="$group[count(. | key('transports-by-assortment', concat(destination, '|', assortment))[1]) = 1]">
<assortment>
<name>
<xsl:value-of select="assortment"/>
</name>
<!-- process this subgroup -->
<xsl:for-each select="key('transports-by-assortment', concat(destination, '|', assortment))" >
<row>
<!-- not sure what goes in here -->
<totalQuantity>
<xsl:value-of select="quantity"/>
</totalQuantity>
</row>
</xsl:for-each>
</assortment>
</xsl:for-each>
</Destination>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Issue with Accumulator in XSLT3.0

I have the below data from one data source,
<Reference>
<Worker>
<Employee_ID>1234</Employee_ID>
<Salary_Type>H</Salary_Type>
</Worker>
<Worker>
<Employee_ID>5678</Employee_ID>
<Salary_Type>H</Salary_Type>
</Worker>
</Reference>
the below data come from another data source,
<root>
<row>
<ID>1234</ID>
<ADDR>ABC</ADDR>
<PHONE>9999999998</PHONE>
<SAL>S</SAL>
</row>
<row>
<ID>5678</ID>
<ADDR>ABD</ADDR>
<PHONE>9999999999</PHONE>
<SAL>S</SAL>
</row>
</root>
I have merged the data from these data sources and have the following data,
<root>
<Reference>
<Worker>
<EmployeeID>1234</Employee_ID>
<SalaryType>H</Salary_Type>
</Worker>
<Worker>
<EmployeeID>5678</Employee_ID>
<SalaryType>H</Salary_Type>
</Worker>
</Reference>
<root>
<row>
<ID>1234</ID>
<ADDR>ABC</ADDR>
<PHONE>9999999998</PHONE>
<SAL>S</SAL>
</row>
<row>
<ID>5678</ID>
<ADDR>ABD</ADDR>
<PHONE>9999999999</PHONE>
<SAL>S</SAL>
</row>
</root>
</root>
In XSLT3.0 I m trying to replace the value of <SAL> with value of <SalaryType> with the following code, buts its not working.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="3.0">
<xsl:mode streamable="no" on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:accumulator name="EIDKey" as="xs:string" initial-value="''" streamable="yes">
<xsl:accumulator-rule match="/root/Reference/Worker/EmployeeID/text()" select="."/>
</xsl:accumulator>
<xsl:accumulator name="PayTypeLookup" as="map(xs:string,xs:string)" initial-value="map {}"
streamable="yes">
<xsl:accumulator-rule match="/root/Reference/Worker/SalaryType/text()"
select="map:put($value, accumulator-before('EIDKey'), string(.))"/>
</xsl:accumulator>
<xsl:template match="/">
<root>
<xsl:for-each select="root/root/row">
<row>
<xsl:apply-templates/>
</row>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="SAL">
<xsl:variable name="PayType" select="normalize-space(.)"/>
<SAL><xsl:value-of select="accumulator-before('PayTypeLookup')($PayType)"/></SAL>
</xsl:template>
</xsl:stylesheet>
I need the final output like below, can you please help? Thanks in advance
<root>
<row>
<ID>1234</ID>
<ADDR>ABC</ADDR>
<PHONE>9999999998</PHONE>
<SAL>H</SAL>
</row>
<row>
<ID>5678</ID>
<ADDR>ABD</ADDR>
<PHONE>9999999999</PHONE>
<SAL>H</SAL>
</row>
</root>
I think you need another accumulator
<xsl:accumulator name="rowID" as="xs:string" initial-value="''" streamable="yes">
<xsl:accumulator-rule match="/root/root/row/ID/text()" select="."/>
</xsl:accumulator>
and then you can use
<xsl:template match="SAL">
<SAL><xsl:value-of select="accumulator-before('PayTypeLookup')(accumulator-before('rowID'))"/></SAL>
</xsl:template>

XSLT combine multiple list from one file to table

I need help with merging several tables of one file into one output.
The files that I get processed perfectly according to the XSLT schema that is listed below. But today I received an interesting file that was not processed as I wanted.
source file
<?xml version="1.0" encoding="utf-8"?>
<extdata user="test">
<scheme name="Order">
<data>
<s>
<d name="CashOrder">
<f name="ActionDate" type="Date" />
<f name="AddressId" type="String" />
<f name="CompanyId" type="String" />
</d>
</s>
<o>
<d name="CashOrder">
<r>
<f>2022-01-24T00:00:00</f>
<f>10234</f>
<f>10234</f>
</r>
<r>
<f>2022-01-24T00:00:00</f>
<f>52817</f>
<f>52817</f>
</r>
<r>
<f>2022-01-24T00:00:00</f>
<f>58259</f>
<f>58259</f>
</r>
</d>
</o>
</data>
<data>
<s>
<d name="OrderOption">
<f name="OrderDate" type="Date" />
<f name="OrderNumber" type="String" />
</d>
</s>
<o>
<d name="OrderOption">
<r>
<f>2022-01-24T00:00:00</f>
<f>WE30395.c17639</f>
</r>
<r>
<f>2022-01-24T00:00:00</f>
<f>WE30395.c17639</f>
</r>
<r>
<f>2022-01-24T00:00:00</f>
<f>WE30395.c17639</f>
</r>
</d>
</o>
</data>
</scheme>
</extdata>
my XSLT (here they helped me with writing.there was a problem with the recursive processing of the hierarchy)
<?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="/extdata/scheme">
<ValueTable xmlns="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:for-each select="data/s/d/f | data/s/d/d/f">
<column>
<Name xsi:type="xs:string">
<xsl:value-of select="#name" />
</Name>
<ValueType>
<xsl:if test="#type= 'String'">
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</xsl:if>
<xsl:if test="#type= 'Date'">
<Type>xs:dateTime</Type>
<DateQualifiers>
<DateFractions>DateTime</DateFractions>
</DateQualifiers>
</xsl:if>
<xsl:if test="#type= 'Decimal' or #type= 'Currency'">
<Type>xs:decimal</Type>
<NumberQualifiers>
<Digits>20</Digits>
<FractionDigits>4</FractionDigits>
<AllowedSign>Any</AllowedSign>
</NumberQualifiers>
</xsl:if>
<xsl:if test="#type= 'Integer'">
<Type>xs:decimal</Type>
<NumberQualifiers>
<Digits>20</Digits>
<FractionDigits>0</FractionDigits>
<AllowedSign>Any</AllowedSign>
</NumberQualifiers>
</xsl:if>
</ValueType>
</column>
</xsl:for-each>
<xsl:for-each select="data/o/d/r">
<row>
<xsl:for-each select=".//f">
<Value>
<xsl:value-of select="." />
</Value>
</xsl:for-each>
</row>
</xsl:for-each>
</ValueTable>
</xsl:template>
</xsl:stylesheet>
output now
<ValueTable xmlns="http://v8.1c.ru/8.1/data/core"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<column>
<Name xsi:type="xs:string">ActionDate</Name>
<ValueType>
<Type>xs:dateTime</Type>
<DateQualifiers>
<DateFractions>DateTime</DateFractions>
</DateQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">AddressId</Name>
<ValueType>
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">CompanyId</Name>
<ValueType>
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">OrderDate</Name>
<ValueType>
<Type>xs:dateTime</Type>
<DateQualifiers>
<DateFractions>DateTime</DateFractions>
</DateQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">OrderNumber</Name>
<ValueType>
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</ValueType>
</column>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>10234</Value>
<Value>10234</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>52817</Value>
<Value>52817</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>58259</Value>
<Value>58259</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>WE30395.c17639</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>WE30395.c17639</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>WE30395.c17639</Value>
</row>
</ValueTable>
need
<ValueTable xmlns="http://v8.1c.ru/8.1/data/core"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<column>
<Name xsi:type="xs:string">ActionDate</Name>
<ValueType>
<Type>xs:dateTime</Type>
<DateQualifiers>
<DateFractions>DateTime</DateFractions>
</DateQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">AddressId</Name>
<ValueType>
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">CompanyId</Name>
<ValueType>
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">OrderDate</Name>
<ValueType>
<Type>xs:dateTime</Type>
<DateQualifiers>
<DateFractions>DateTime</DateFractions>
</DateQualifiers>
</ValueType>
</column>
<column>
<Name xsi:type="xs:string">OrderNumber</Name>
<ValueType>
<Type>xs:string</Type>
<StringQualifiers>
<Length>150</Length>
<AllowedLength>Variable</AllowedLength>
</StringQualifiers>
</ValueType>
</column>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>10234</Value>
<Value>10234</Value>
<Value>2022-01-24T00:00:00</Value>
<Value>WE30395.c17639</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>52817</Value>
<Value>52817</Value>
<Value>2022-01-24T00:00:00</Value>
<Value>WE30395.c17639</Value>
</row>
<row>
<Value>2022-01-24T00:00:00</Value>
<Value>58259</Value>
<Value>58259</Value>
<Value>2022-01-24T00:00:00</Value>
<Value>WE30395.c17639</Value>
</row>
</ValueTable>
Briefly, the structure can be described as follows
I mean this is how I got it
<table>
<header>
<column1>
<column2>
<column3>
<column4>
<column5>
<header>
<row>
value1
value2
value3
</row>
<row>
value4
value5
</row>
</table>
but it should be like this
<table>
<header>
<column1>
<column2>
<column3>
<column4>
<column5>
</header>
<row>
value1
value2
value3
value4
value5
</row>
</table>
The example is rather confusing because many values are identical and it's hard to see what goes where. It's also not clear if there can be more data elements and if so, what should the result be then.
AFAICT, this produces the expected result for the given example. For clarity, I have omitted the header part, which seems to be working fine.
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/extdata/scheme">
<ValueTable xmlns="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- header -->
<!-- data -->
<xsl:for-each select="data[1]/o/d/r">
<row>
<xsl:variable name="i" select="position()"/>
<xsl:for-each select="//data/o/d/r[$i]/f">
<Value>
<xsl:value-of select="." />
</Value>
</xsl:for-each>
</row>
</xsl:for-each>
</ValueTable>
</xsl:template>
</xsl:stylesheet>

XSLT group every nth item in to new group

I have done few XSLT in the past, but I am facing challenge in this.
I am working with PLC tag, for each tag i am getting three rowset node, so after every three Rowset i need to create new "Row" group.
Updated with XSLT
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<Rowsets >
<Rowset>
<Row>
<DateTime>2021-07-05T07:33:38</DateTime>
<WC_ID>0001</WC_ID>
</Row>
</Rowset>
<Rowset>
<Row>
<DateTime>2021-07-05T07:33:38</DateTime>
<Tag1_Good>6817</Tag1_Good>
</Row>
</Rowset>
<Rowset>
<Row>
<DateTime>2021-07-05T07:33:38</DateTime>
<Tag1_Bad>0</Tag1_Bad>
</Row>
</Rowset>
<Rowset>
<Row>
<DateTime>2021-07-05T07:33:38</DateTime>
<WC_ID>0002</WC_ID>
</Row>
</Rowset>
<Rowset>
<Row>
<DateTime>2021-07-05T07:33:38</DateTime>
<Tag2_Good>6800</Tag2_Good>
</Row>
</Rowset>
<Rowset>
<Row>
<DateTime>2021-07-05T07:33:38</DateTime>
<Tag2_Bad>0</Tag2_Bad>
</Row>
</Rowset>
</Rowsets>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<Rowset>
<Row>
<WC_ID>0001</WC_ID>
<Tag1_Good>6817</Tag1_Good>
<Tag1_Bad>0</Tag1_Bad>
</Row>
<Row>
<WC_ID>0002</WC_ID>
<Tag1_Good>6800</Tag1_Good>
<Tag1_Bad>0</Tag1_Bad>
</Row>
</Rowset>
My XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Rowsets >
<xsl:variable name="batchSize" select="3"/>
<Rowset>
<xsl:for-each select="/Rowsets/Rowset[position() mod $batchSize >= 0]"
<Row>
<xsl:value-of select="Row/*[2]" />
</Row>
</xsl:for-each>
</Rowset>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
I am not able to make this into a new group
Use this:
<Rowsets>
<xsl:variable name="batchSize" select="3"/>
<Rowset>
<xsl:for-each select="/Rowsets/Rowset">
<xsl:variable name="pos" select="position()"/>
<xsl:if test="$pos mod $batchSize = 0">
<Row>
<WC_ID>
<xsl:value-of select="/Rowsets/Rowset[$pos - 2]/Row/*[2]"/>
</WC_ID>
<Tag1_Good>
<xsl:value-of select="/Rowsets/Rowset[$pos - 1]/Row/*[2]"/>
</Tag1_Good>
<Tag1_Bad>
<xsl:value-of select="/Rowsets/Rowset[$pos]/Row/*[2]"/>
</Tag1_Bad>
</Row>
</xsl:if>
</xsl:for-each>
</Rowset>
</Rowsets>

how to combine elements in multiple rows using a repeating element

Below is my Input xml and I want to use xslt 1.0 to transform into below specified xml.
Element ref needs to be used as key to merge and create the array in the output
Input xml :
<Input>
<row>
<Name>ABC</Name>
<Ref>12345</Ref>
<Status>O</Status>
<Currency>USD</Currency>
<Date>2016-05-16</Date>
</row>
<row>
<Name>ABC</Name>
<Ref>12345</Ref>
<Status>O</Status>
<Currency>AUD</Currency>
<Date>2016-05-01</Date>
</row>
<row>
<Name>XYZ</Name>
<Ref>54321</Ref>
<Status>O</Status>
<Currency>AUD</Currency>
<Date>2016-03-01</Date>
</row>
<row>
<Name>XYZ</Name>
<Ref>54321</Ref>
<Status>O</Status>
<Currency>USD</Currency>
<Date>2016-05-01</Date>
</row>
</Input>
Output Xml: using the ref element in the request the there will be only two row elements in the output xml, but the values that are different are clubbed in a another sub node.
<Output>
<row>
<Name>ABC</Name>
<Ref>12345</Ref>
<Status>O</Status>
<Details>
<Detail>
<Currency>USD</Currency>
<Date>2016-05-16</Date>
</Detail>
<Detail>
<Currency>AUD</Currency>
<Date>2016-05-01</Date>
</Detail>
</Details>
</row>
<row>
<Name>XYZ</Name>
<Ref>54321</Ref>
<Status>O</Status>
<Details>
<Detail>
<Currency>AUD</Currency>
<Date>2016-03-01</Date>
</Detail>
<Detail>
<Currency>USD</Currency>
<Date>2016-05-01</Date>
</Detail>
</Details>
</row>
</Output>
Any help ?
Tried using the below xslt , but the if condition isn't working
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<xsl:output method="xml" version="1.0"/>
<xsl:template match="/">
<xsl:element name="Output">
<xsl:apply-templates select="Input"/>
</xsl:element>
</xsl:template>
<xsl:template match="Input">
<xsl:for-each select="row">
<xsl:if test="(following-sibling::Ref = /Ref)">
<xsl:element name="row">
<xsl:element name="Name">
<xsl:value-of select="Name" />
</xsl:element>
<xsl:element name="Ref">
<xsl:value-of select="Ref" />
</xsl:element>
<xsl:element name="Status">
<xsl:value-of select="Status" />
</xsl:element>
<xsl:element name="Details">
<xsl:element name="Detail">
<xsl:element name="Currency">
<xsl:value-of select="Currency" />
</xsl:element>
<xsl:element name="Date">
<xsl:value-of select="Date" />
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
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:key name="kRowByRefNameStat" match="row" use="concat(Name, '|', Ref, '|', Status)"/>
<xsl:template match="/*">
<Output>
<xsl:apply-templates select=
"row[generate-id()
=
generate-id(key('kRowByRefNameStat',
concat(Name, '|', Ref, '|', Status))[1])]"/>
</Output>
</xsl:template>
<xsl:template match="row">
<row>
<xsl:copy-of select="Name | Ref | Status"/>
<Details>
<xsl:apply-templates mode="inGroup" select=
"key('kRowByRefNameStat', concat(Name, '|', Ref, '|', Status))"/>
</Details>
</row>
</xsl:template>
<xsl:template match="row" mode="inGroup">
<Detail><xsl:copy-of select="Currency | Date"/></Detail>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Input>
<row>
<Name>ABC</Name>
<Ref>12345</Ref>
<Status>O</Status>
<Currency>USD</Currency>
<Date>2016-05-16</Date>
</row>
<row>
<Name>ABC</Name>
<Ref>12345</Ref>
<Status>O</Status>
<Currency>AUD</Currency>
<Date>2016-05-01</Date>
</row>
<row>
<Name>XYZ</Name>
<Ref>54321</Ref>
<Status>O</Status>
<Currency>AUD</Currency>
<Date>2016-03-01</Date>
</row>
<row>
<Name>XYZ</Name>
<Ref>54321</Ref>
<Status>O</Status>
<Currency>USD</Currency>
<Date>2016-05-01</Date>
</row>
</Input>
produces the wanted, correct result:
<Output>
<row>
<Name>ABC</Name>
<Ref>12345</Ref>
<Status>O</Status>
<Details>
<Detail>
<Currency>USD</Currency>
<Date>2016-05-16</Date>
</Detail>
<Detail>
<Currency>AUD</Currency>
<Date>2016-05-01</Date>
</Detail>
</Details>
</row>
<row>
<Name>XYZ</Name>
<Ref>54321</Ref>
<Status>O</Status>
<Details>
<Detail>
<Currency>AUD</Currency>
<Date>2016-03-01</Date>
</Detail>
<Detail>
<Currency>USD</Currency>
<Date>2016-05-01</Date>
</Detail>
</Details>
</row>
</Output>
Explanation:
Muenchian grouping using composite-keys
Modes

XSLT to remove duplicate values while concating

My XML looks like folloing:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<BATCHES>
<item>
<Material>1000000079</Material>
<Description>330 Bulk</Description>
<Tank>T123</Tank>
<Batch>2013225287</Batch>
<Quantity>510</Quantity>
</item>
<item>
<Material>1000000079</Material>
<Description>330 Bulk</Description>
<Tank>T123</Tank>
<Batch>2013225301</Batch>
<Quantity>520</Quantity>
</item>
<item>
<Material>1000000196</Material>
<Description>340R Bulk</Description>
<Tank>T700</Tank>
<Batch>1000188378</Batch>
<Quantity>510</Quantity>
</item>
<item>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Tank>T515</Tank>
<Batch>2013180125</Batch>
<Quantity>300</Quantity>
</item>
<item>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Tank>T515</Tank>
<Batch>2013203124</Batch>
<Quantity>200</Quantity>
</item>
<item>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Tank>T515</Tank>
<Batch>2013214839</Batch>
<Quantity>700</Quantity>
</item>
<item>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Tank>T517</Tank>
<Batch>2013214342</Batch>
<Quantity>890</Quantity>
</item>
</BATCHES>
My original XSLT look like this:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:template match="/">
<Rowsets>
<Rowset>
<xsl:variable name="materials" select=".//item[Tank!='RECV' and Tank!='PARK'] "/>
<xsl:for-each select="$materials">
<xsl:if test="generate-id(.)= generate-id($materials[Material=current()/Material])">
<Row>
<Material>
<xsl:value-of select="Material"/>
</Material>
<Description>
<xsl:value-of select="Description"/>
</Description>
<Value>
<xsl:for-each select="$materials[Material=current()/Material]/Tank">
<xsl:if test="node()">
<xsl:value-of select="concat(.,'||')"/>
</xsl:if>
</xsl:for-each>
</Value>
</Row>
</xsl:if>
</xsl:for-each>
</Rowset>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
The result of this XSLT looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Rowsets>
<Rowset>
<Row>
<Material>1000000079</Material>
<Description>330 Bulk</Description>
<Value>T123||T123||</Value>
</Row>
<Row>
<Material>1000000196</Material>
<Description>340R Bulk</Description>
<Value>T700||</Value>
</Row>
<Row>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Value>T515||T517||</Value>
</Row>
</Rowset>
</Rowsets>
I wanted to remove duplicate tanks while concatinating it in Value field. So I changed my XSLT to follwoing:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:template match="/">
<Rowsets>
<Rowset>
<xsl:variable name="materials" select=".//item[Tank!='RECV' and Tank!='PARK' and Quantity > 500]"/>
<xsl:for-each select="$materials">
<xsl:if test="generate-id(.)= generate-id($materials[Material=current()/Material])">
<Row>
<Material>
<xsl:value-of select="Material"/>
</Material>
<Description>
<xsl:value-of select="Description"/>
</Description>
<Value>
<xsl:for-each select="$materials[Material=current()/Material]/Tank[not(.=preceding::Tank)]">
<xsl:if test="node()">
<xsl:value-of select="concat(.,'||')"/>
</xsl:if>
</xsl:for-each>
</Value>
</Row>
</xsl:if>
</xsl:for-each>
</Rowset>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
My result now looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Rowsets>
<Rowset>
<Row>
<Material>1000000079</Material>
<Description>330 Bulk</Description>
<Value>T123||</Value>
</Row>
<Row>
<Material>1000000196</Material>
<Description>340R Bulk</Description>
<Value>T700||</Value>
</Row>
<Row>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Value>T517||</Value>
</Row>
</Rowset>
</Rowsets>
It removed the duplicate tank T123 for material 1000000079 but for material 1000002754, it even removed T515which should appear in Value field as its quantity is greater than 500 for folloing:
<item>
<Material>1000002754</Material>
<Description>43 Bulk</Description>
<Tank>T515</Tank>
<Batch>2013214839</Batch>
<Quantity>700</Quantity>
</item>
what am I doing wrong here?
Ok I see your problem.
For this a key based solution Using Keys to Group: The Muenchian Method:
Try this:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:key name="kMaterial" match="item[Tank!='RECV' and Tank!='PARK' and Quantity > 500]"
use="Material"/>
<xsl:key name="kMaterialTank" match="item[Tank!='RECV' and Tank!='PARK' and Quantity > 500]"
use="concat(Material,'|', Tank)"/>
<xsl:template match="/">
<Rowsets>
<Rowset>
<xsl:variable name="materials" select=".//item[Tank!='RECV' and Tank!='PARK'] "/>
<xsl:for-each select="//item[ generate-id(.) = generate-id( key('kMaterial', Material)[1]) ]" >
<xsl:variable name="m" select="Material" />
<Row>
<Material>
<xsl:value-of select="Material"/>,<xsl:value-of select="$m"/>
</Material>
<Description>
<xsl:value-of select="Description"/>
</Description>
<Value>
<xsl:for-each select="//item[ generate-id(.) =
generate-id( key('kMaterialTank', concat( $m,'|', Tank))[1])]" mode="tank" >
<xsl:apply-templates select="." mode="tank" />
</xsl:for-each>
</Value>
</Row>
</xsl:for-each>
</Rowset>
</Rowsets>
</xsl:template>
<xsl:template match="item" mode="tank" >
<xsl:value-of select="Tank"/>
<xsl:text>||</xsl:text>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
<?xml version="1.0" encoding="UTF-8"?>
<Rowsets>
<Rowset>
<Row>
<Material>1000000079,1000000079</Material>
<Description>330 Bulk</Description>
<Value>T123||</Value>
</Row>
<Row>
<Material>1000000196,1000000196</Material>
<Description>340R Bulk</Description>
<Value>T700||</Value>
</Row>
<Row>
<Material>1000002754,1000002754</Material>
<Description>43 Bulk</Description>
<Value>T515||T517||</Value>
</Row>
</Rowset>
</Rowsets>