I am trying to transform this XML file :
<database name="sql_sales">
<!-- Table Customer -->
<table name="Customer">
<column name="CustomerID">1</column>
<column name="Name">Roth Farrell</column>
<column name="Age">50</column>
<column name="Country">Canada</column>
<column name="City">Fermont</column>
<column name="Signup_date">2013-03-09 16:34:13</column>
<column name="Channel">Coupon</column>
</table>
<table name="Customer">
<column name="CustomerID">3</column>
<column name="Name">Randall Mosley</column>
<column name="Age">20</column>
<column name="Country">Belgium</column>
<column name="City">Leuze</column>
<column name="Signup_date">2012-03-26 04:02:37</column>
<column name="Channel">SEO</column>
</table>
</database>
to this format:
<dataset>
<Cust CustomerID="1" Name="Roth Farrell" Age="50" Country="Canada" City="Fermont" Signup_date="2012-04-26 17:34:13.0" Channel="Coupon"/>
<Cust CustomerID="3" Name="Randall Mosley" Age="20" Country="Belgium" City="Leuze" Signup_date="2011-05-14 05:02:37.0" Channel="SEO"/>
</dataset>
I am new to XSLT, How can I do this with XSLT?
thanks
Use
<xsl:template match="database">
<dataset>
<xsl:apply-templates/>
</dataset>
</xsl:template>
<xsl:template match="table[#name = 'Customer']">
<Cust>
<xsl:apply-templates select="column"/>
</Cust>
</xsl:template>
<xsl:template match="column">
<xsl:attribute name="{#name}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
Related
I have an XML, i am trying to do kind of group by with XLST 1.0.
Input XMl:
<Rowset>
<Row>
<col1>7:00</col1>
<name>Shell Test</name>
<passCount>1</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>7:00</col1>
<name>Stroke Test</name>
<passCount>1</passCount>
<failCount>1</failCount>
</Row>
<Row>
<col1>7:00</col1>
<name>Shutoff Test</name>
<passCount>0</passCount>
<failCount>1</failCount>
</Row>
<Row>
<col1>8:00</col1>
<name>Shell Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>8:00</col1>
<name>Stroke Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>8:00</col1>
<name>Shutoff Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>9:00</col1>
<name>Shell Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>9:00</col1>
<name>Stroke Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>9:00</col1>
<name>Shutoff Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
</Rowset>
outPutXMl:
<?xml version="1.0" encoding="UTF-8"?>
<Row>
<element>
<TestName>Shell Test</TestName>
</element>
<element>
<TestName>Stroke Test</TestName>
</element>
<element>
<TestName>Shutoff Test</TestName>
</element>
</Row>
<Row>
<element>
<Time>7:00</Time>
<Pass>1</Pass>
<Fail>0</Fail>
<Pass>1</Pass>
<Fail>1</Fail>
<Pass>0</Pass>
<Fail>1</Fail>
</element>
<element>
<Time>8:00</Time>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
</element>
<element>
<Time>9:00</Time>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
</element>
</Row>
i am facing problem in extracting all the values from group in second for-each-group
my xslt 1.0 is as below:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="col1name" match="Row" use="name" />
<xsl:key name="time" match="Row" use="col1" />
<xsl:template match="/Rowsets/Rowset">
<Row>
<xsl:for-each select="Row[generate-id() = generate-id(key('col1name', name)[1])]">
<TestName><xsl:value-of select="name"/></TestName>
</xsl:for-each>
</Row>
<xsl:for-each select="Row">
<Time><xsl:value-of select="col1"/></Time>
<xsl:apply-templates select="/Rowsets/Rowset"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="/Rowsets/Rowset">
<xsl:for-each select="Row[generate-id() = generate-id(key('time', col1)[*])]">
<Pass><xsl:value-of select="passCount"/></Pass>
<Fail><xsl:value-of select="failCount"/></Fail>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
with XSLT 2.0 i could achieve this, but as per my requirement my application only supports XSLT 1.0
XSLT 2.0 Code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Rowsets/Rowset">
<Row>
<xsl:for-each-group select="Row" group-by="name">
<element>
<TestName>
<xsl:value-of select="name"/>
</TestName>
</element>
</xsl:for-each-group>
</Row>
<Row>
<xsl:for-each-group select="Row" group-by="col1">
<element>
<Time>
<xsl:value-of select="col1"/>
</Time>
<xsl:for-each select="current-group()">
<Pass>
<xsl:value-of select="passCount"/>
</Pass>
<Fail>
<xsl:value-of select="failCount"/>
</Fail>
</xsl:for-each>
</element>
</xsl:for-each-group>
</Row>
</xsl:template>
</xsl:stylesheet>
Can someone please help in replicating the output with XSLT 1.0
Use the two keys for Muenchian grouping:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="by-name" match="Row" use="name"/>
<xsl:key name="by-col1" match="Row" use="col1"/>
<xsl:template match="/Rowsets/Rowset">
<Row>
<xsl:for-each select="Row[generate-id() = generate-id(key('by-name', name)[1])]">
<element>
<TestName>
<xsl:value-of select="name"/>
</TestName>
</element>
</xsl:for-each>
</Row>
<Row>
<xsl:for-each select="Row[generate-id() = generate-id(key('by-col1', col1)[1])]">
<element>
<Time>
<xsl:value-of select="col1"/>
</Time>
<xsl:for-each select="key('by-col1', col1)">
<Pass>
<xsl:value-of select="passCount"/>
</Pass>
<Fail>
<xsl:value-of select="failCount"/>
</Fail>
</xsl:for-each>
</element>
</xsl:for-each>
</Row>
</xsl:template>
</xsl:stylesheet>
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>
input:
<DS>
<TL>
<msg>
<output_getquerydata>
<queries>
<query name="q1">
<parameters>
<parameter name="id">906OREA</parameter>
</parameters>
<results>
<record>
<column name="actionState">sdss</column>
</record>
</results>
</query>
<query name="q2">
<parameters>
<parameter name="resCode">CTL</parameter>
<parameter name="prodCode">89CMID</parameter>
<parameter name="pos">1,2,4,3</parameter>
</parameters>
<results>
<record id="1">
<column name="position">1</column>
<column name="ExternalProductId"/>
</record>
<record id="9">
<column name="position"/>
<column name="ExternalProductId">316442</column>
</record>
</results>
</query>
<query name="q2">
<parameters>
<parameter name="resCode">CTL</parameter>
<parameter name="prodCode">91VPRM</parameter>
<parameter name="pos">1,2,4,3</parameter>
</parameters>
<results>
<record id="1">
<column name="position"/>
<column name="ExternalProductId">316495</column>
</record>
</results>
</query>
</queries>
</output_getquerydata>
</msg>
<TL>
<ArticleNr>89CMID</ArticleNr>
</TL>
<TL>
<ArticleNr>89CMID</ArticleNr>
</TL>
<TL>
<ArticleNr>89CMID</ArticleNr>
</TL>
<TL>
<ArticleNr>91VPRM</ArticleNr>
</TL>
</TL>
</DS>
XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="Article" match="tLoading" use="./ArticleNr"/>
<xsl:key name="prod" match="query[#name='q2']/results/record" use="./column[#name='ExternalProductId'][node()]"/>
<xsl:template match="DS">
<msglist>
<xsl:for-each select="./TL[./msg/output_getquerydata/queries/query/results/record/column[#name='actionState'] !='finished'] ">
<xsl:variable name="distinctArticle" select="//TL[string(ArticleNr)][count(. | key('Article',ArticleNr)[1]) = 1]"/>
<msg>
<xsl:for-each select="$distinctArticle">
<load-part>
<!--I need here the value from column[#name='ExtPR'], that has parameter[#name='prodCode']=the current TL articleNr node.
-->
<productId>
<xsl:value-of select="key('prod',column[#name='ExternalProductId'])"/>
</productId>
<!--something-->
</load-part>
</xsl:for-each>
</msg>
</xsl:for-each>
</msglist>
</xsl:template>
</xsl:stylesheet>
Desired OUTPUT:
<msglist>
<msg>
<load-part>
<productId>316442</productId>
</load-part>
<load-part>
<productId>316442</productId>
</load-part>
<load-part>
<productId>316442</productId>
</load-part>
<load-part>
<productId>316495</productId>
</load-part>
</msg>
</msglist>
I need in the productID node, the value from column[#name='ExternalProductId'], that has parameter[#name='prodCode']=the current <TL><ArticleNr> node.
I know that this 'for each' code that I've put, returns only two values, because i'm searching for the distinct values, so I was thinking that I will try with a key, but i'm not sure what I am missing.
Thank you
edited for the correct output values
Firstly, your Article key looks wrong (in the context of your question) as there are no tLoading elements in your XML. So it should be this...
<xsl:key name="Article" match="TL" use="ArticleNr"/>
But to answer your direct question, you need to define your prod key like so
<xsl:key name="prod"
match="query[#name='q2']/results/record"
use="../../parameters/parameter[#name='prodCode']"/>
Then, to look it up, do this...
<xsl:value-of select="key('prod', ArticleNr)/column[#name='ExternalProductId']"/>
Or maybe this, as they "q2" query has two ExternalProductIds...
<xsl:value-of select="key('prod', ArticleNr)/column[#name='ExternalProductId'][. != '']"/>
Try this XSLT (which retains your distinct check, and so only outputs two rows)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="Article" match="TL" use="ArticleNr"/>
<xsl:key name="prod" match="query[#name='q2']/results/record" use="../../parameters/parameter[#name='prodCode']"/>
<xsl:template match="DS">
<msglist>
<xsl:for-each select="./TL[./msg/output_getquerydata/queries/query/results/record/column[#name='actionState'] !='finished'] ">
<xsl:variable name="distinctArticle" select="//TL[string(ArticleNr)][count(. | key('Article',ArticleNr)[1]) = 1]"/>
<msg>
<xsl:for-each select="$distinctArticle">
<load-part>
<productId>
<xsl:value-of select="ArticleNr" />
<xsl:text> - </xsl:text>
<xsl:value-of select="key('prod', ArticleNr)/column[#name='ExternalProductId'][. != '']"/>
</productId>
</load-part>
</xsl:for-each>
</msg>
</xsl:for-each>
</msglist>
</xsl:template>
</xsl:stylesheet>
I had to group a xml document in xslt 1.0 using Oracle Service Bus.
This is the sample input file(Simplified):
<?xml version="1.0" encoding="UTF-8"?>
<EMailData>
<property name="A">
<property name="B">
<property name="C">
<row>
<property name="C1">
<value>ValC1</value>
</property>
<property name="C2">
<value>ValC2</value>
</property>
<property name="C3">
<value>Valc3</value>
</property>
<property name="C4">
<value>Valc4</value>
</property>
</row>
</property>
<property name="C">
<row>
<property name="C1">
<value>ValC1</value>
</property>
<property name="C2">
<value>ValC2</value>
</property>
<property name="C3">
<value>Valc3</value>
</property>
<property name="C4">
<value>Valc4</value>
</property>
</row>
</property>
<property name="D">
<row>
<property name="D1">
<value>ValD1</value>
</property>
<property name="D2">
<value>VALd2</value>
</property>
<property name="D3-InnerElement"> //Need to Group this too
<row>
<property name="Status">
<value>Status122</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status123</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status124</value>
</property>
</row>
</property>
</row>
</property>
<property name="D">
<row>
<property name="D1">
<value>ValD1</value>
</property>
<property name="D2">
<value>VALd2</value>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status122</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status123</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status124</value>
</property>
</row>
</property>
</row>
</property>
</property>
</property>
</EMailData>
My XSLT Logic:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:key name="group" match="/*/*/*/property" use="#name"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*/*[property[#name]]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="*[generate-id() = generate-id(key('group', #name)[1])]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="key('group', #name)/*"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<!--Change for Inner Hierarchy-->
<xsl:key name="inner-group" match="/*/*/*/*/property" use="#name"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*/*/*[property[#name]]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="*[generate-id() = generate-id(key('inner-group', #name)[1])]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="key('inner-group', #name)/*"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Anticipated o/p
<?xml version="1.0" encoding="UTF-8"?>
<EMailData>
<property name="A">
<property name="B">
<property name="C">
<row>
<property name="C1">
<value>ValC1</value>
</property>
<property name="C2">
<value>ValC2</value>
</property>
<property name="C3">
<value>Valc3</value>
</property>
<property name="C4">
<value>Valc4</value>
</property>
</row>
<row>
<property name="C1">
<value>ValC1</value>
</property>
<property name="C2">
<value>ValC2</value>
</property>
<property name="C3">
<value>Valc3</value>
</property>
<property name="C4">
<value>Valc4</value>
</property>
</row>
</property>
<property name="D">
<row>
<property name="D1">
<value>ValD1</value>
</property>
<property name="D2">
<value>VALd2</value>
</property>
<property name="D3-InnerElement"> //Need to Group this too
<row>
<property name="Status">
<value>Status122</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status123</value>
</property>
</row>
<row>
<property name="Status">
<value>Status124</value>
</property>
</row>
</property>
</row>
<row>
<property name="D1">
<value>ValD1</value>
</property>
<property name="D2">
<value>VALd2</value>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status122</value>
</property>
</row>
<row>
<property name="Status">
<value>Status123</value>
</property>
</row>
<row>
<property name="Status">
<value>Status124</value>
</property>
</row>
</property>
</row>
</property>
</property>
</property>
</EMailData>
But The D3-innerelement is not grouped. Tell me Where I went wrong!!
o/p For my XSLT
<?xml version="1.0" encoding="UTF-8"?>
<EMailData>
<property name="A">
<property name="B">
<property name="C">
<row>
<property name="C1">
<value>ValC1</value>
</property>
<property name="C2">
<value>ValC2</value>
</property>
<property name="C3">
<value>Valc3</value>
</property>
<property name="C4">
<value>Valc4</value>
</property>
</row>
<row>
<property name="C1">
<value>ValC1</value>
</property>
<property name="C2">
<value>ValC2</value>
</property>
<property name="C3">
<value>Valc3</value>
</property>
<property name="C4">
<value>Valc4</value>
</property>
</row>
</property>
<property name="D">
<row>
<property name="D1">
<value>ValD1</value>
</property>
<property name="D2">
<value>VALd2</value>
</property>
<property name="D3-InnerElement"> //Need to Group this too
<row>
<property name="Status">
<value>Status122</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status123</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status124</value>
</property>
</row>
</property>
</row>
<row>
<property name="D1">
<value>ValD1</value>
</property>
<property name="D2">
<value>VALd2</value>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status122</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status123</value>
</property>
</row>
</property>
<property name="D3-InnerElement">
<row>
<property name="Status">
<value>Status124</value>
</property>
</row>
</property>
</row>
</property>
</property>
</property>
</EMailData>
Thanks in Advance!
And here goes the solution using Muenchian's grouping:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:key name="group" match="property" use="#name"/>
<xsl:template match="/EMailData/property/property | /EMailData/property/property/property/row">
<xsl:variable name="id" select="generate-id()"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="property[count(. | key('group', #name)[$id = generate-id(parent::*)][1]) = 1]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="key('group', #name)[$id = generate-id(parent::*)]/*"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This solution isn't based on Muenchian's grouping, but thought it would be helpful:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*[property]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="property[not(#name = preceding-sibling::property/#name)]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="../property[#name = current()/#name]/*"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Here, the second template is the identity transform template, used to copy all attributes and nodes.
The first template matches elements with at least one property child, or in simple words, "parents of the property elements to be grouped by #name".
You may, as well, change the template match to:
<xsl:template match="/EMailData/property/property | /EMailData/property/property/property/row">
The for-each is on the first property with a specific #name value in the current parent(see the condition using preceding-sibling).
And for every iteration, templates are applied for all the child elements of property elements with the current(for-each element's) #name, i.e., grouping property elements of a single parent by their #name's value.
The same template is called for the inner property elements, grouping even those by #name.
I need to do rather simple thing of iterating over elements in my XML(more explanation under the XML)
<form>
<field index="1" name="field_X_1" group="firstGroup" type="String">
<value>Value of Field X 1 of first group</value>
</field>
<field index="1" name="field_Y_1" group="firstGroup" type="String">
<value>Value of Field Y 1 of first group</value>
</field>
<field index="2" name="field_X_2" group="firstGroup" type="String">
<value>Value of Field X 2 of first group</value>
</field>
<field index="2" name="field_Y_2" group="firstGroup" type="String">
<value>Value of Field Y 2 of first group</value>
</field>
<field index="1" name="field_A_1" group="secondGroup" type="String">
<value>Value of Field A 1 of second group</value>
</field>
<field index="1" name="field_B_1" group="secondGroup" type="String">
<value>Value of Field B 1 of second group</value>
</field>
<field index="2" name="field_A_2" group="secondGroup" type="String">
<value>Value of Field A 2 of second group</value>
</field>
<field index="2" name="field_B_2" group="secondGroup" type="String">
<value>Value of Field B 2 of second group</value>
</field>
</form>
Right now I am iterating over ALL fields of firstGroup and everytime I found that index has changed(by comparing index to index of sibbling) I add new line character).
Problem is I need only to iterate over index (but still keep distiction between firstGroup and secondGroup).
My output should look like
Value of Field X 1 of first group, Value of Field Y 1 of first group
Value of Field X 2 of first group, Value of Field Y 2 of first group
Value of Field A 1 of second group, Value of Field B 1 of second group
Value of Field A 2 of second group, Value of Field B 2 of second group
etc
My XSL now looks like
<xsl:for-each select='/form/field[#group="firstGroup"]'>
<xsl:sort select="#index" order="ascending"></xsl:sort>
<xsl:if test='preceding-sibling::*[#group="firstGroup"][1]/#index != #index and count(preceding-sibling::*) != 0 '>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:value-of select="//field[#name=concat('field_X_', #index,')]/value"/>
<xsl:value-of select="//field[#name=concat('field_Y_', #index,')]/value"/>
</xsl:for-each>
<!-- Differente iteration for second group-->
<xsl:text>
</xsl:text>
<xsl:for-each select='/form/field[#group="firstGroup"]'>
<xsl:sort select="#index" order="ascending"></xsl:sort>
<xsl:if test='preceding-sibling::*[#group="firstGroup"][1]/#index != #index and count(preceding-sibling::*) != 0 '>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:value-of select="//field[#name=concat('field_A_', #index,')]/value"/>
<xsl:value-of select="//field[#name=concat('field_B_', #index,')]/value"/>
</xsl:for-each>
<xsl:output method="text" />
<xsl:template match="form">
<!--* find all the groups *-->
<xsl:variable name="groups">
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="field" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="process-all-groups">
<xsl:with-param name="group-names" select="$groups" />
</xsl:call-template>
</xsl:template>
<xsl:template name="get-group-names">
<xsl:param name="nodes" />
<xsl:param name="result-so-far" />
<xsl:choose>
<xsl:when test="not($nodes)">
<xsl:value-of select="$result-so-far" />
</xsl:when>
<xsl:when test="contains(
concat(' ', $result-so-far),
concat(' ', $nodes[1]/#group, ' '))" >
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="result-so-far" select="$result-so-far" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="result-so-far"
select="concat($result-so-far, $nodes[1]/#group, ' ')" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="process-all-groups">
<xsl:param name="group-names" />
<xsl:variable name="group" select="substring-before($group-names, ' ')"/>
<xsl:if test="not(string-length($group) = 0)">
<xsl:call-template name="index">
<xsl:with-param name="n" select="1" />
<xsl:with-param name="group" select="$group" />
</xsl:call-template>
<xsl:call-template name="process-all-groups">
<xsl:with-param name="group-names"
select="substring-after($group-names, ' ')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="index">
<xsl:param name="n" select="1" />
<xsl:param name="group" />
<xsl:variable name="with-n"
select="/form/field[#group = $group][#index = $n]" />
<xsl:if test="$with-n">
<xsl:for-each select="$with-n">
<xsl:sort use="#index" order="ascending" />
<xsl:value-of select="value" />
<xsl:if test="not(position() = last())">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:call-template name="index">
<xsl:with-param name="n" select="$n + 1" />
<xsl:with-param name="group" select="$group" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="field"></xsl:template>