group width on xslt elements (xslt 1.0) - xslt

ХМL:
<?xml version="1.0" encoding="utf-8" ?>
<page>
<elements>
<element>
<data>
<Styles Name="default">
<Style Url="/css.css" Browser="default" Version="default"/>
</Styles>
</data>
</element>
<element type="Digillect.WB.Web.Elements.Site.SiteStructureElement">
<config StartLevel="0" MaxDepth="3" UseItemVisibility="false">
<monikers>
<moniker store="asdasd"/>
</monikers>
</config>
<data ParentPath="/">
</data>
</element>
<element name="bids">
<config>
<Object Id="1b61995a-6e22-4b09-af5f-9a50cdaa7863"/>
<Object Id="baa1d3df-0510-4f68-8a41-1b9b22587134"/>
</config>
<data>
<Object Id="id2" Name="Paris">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1" Name="Lion">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id3" Name="Berlin">
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
It is necessary to bring the country's selectors sorted by name and get rid of duplicates:
<select>
<option value="http://germany.gr">Germany</option>
<option value="france">France</option>
</select>
That is, if the country meets a few times, in value = specify the id. If once, then specify the link
Description grouped as follows: if a country meets several times
Code:
<div id="france">
<p> Lion</p>
<p>Paris</p>
</div>
If the country meets once - did not write

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="kObjByCountry" match="Object" use="Property[#Name='COUNTRY']"/>
<xsl:template match="/">
<xsl:variable name="vCountries" select=
"/*/*/*/data/Object
[generate-id()
= generate-id(key('kObjByCountry', Property[#Name='COUNTRY'])[1])
]"/>
<select>
<xsl:apply-templates select="$vCountries">
<xsl:sort select="Property[#Name='COUNTRY']"/>
</xsl:apply-templates>
</select>
<xsl:apply-templates select="$vCountries" mode="desc">
<xsl:sort select="Property[#Name='COUNTRY']"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Object">
<xsl:variable name="v2nd"
select="key('kObjByCountry', Property[#Name='COUNTRY'])[2]"/>
<option value="{(#Id[$v2nd]|Property[#Name='WWW'])[1]}">
<xsl:value-of select="Property[#Name='COUNTRY']"/>
</option>
</xsl:template>
<xsl:template mode="desc"
match="Object[key('kObjByCountry', Property[#Name='COUNTRY'])[2]]">
<div id="sity_{#Id}">
<xsl:apply-templates select=
"key('kObjByCountry', Property[#Name='COUNTRY'])
/Property[#Name='NAME']">
<xsl:sort/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="Property[#Name='NAME']">
<p><xsl:value-of select="."/></p>
</xsl:template>
<xsl:template mode="desc"
match="Object[not(key('kObjByCountry', Property[#Name='COUNTRY'])[2])]"/>
</xsl:stylesheet>
when applied on the provided XML document:
<page>
<elements>
<element name="bids">
<data>
<Object Id="id1">
<Property Name="NAME" Order="0">Paris</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1">
<Property Name="NAME" Order="0">Lion</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id2">
<Property Name="NAME" Order="0">Berlin</Property>
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
produces the wanted, correct result -- in which the countries/cities are sorted:
<select>
<option value="id1">France</option>
<option value="http://germany.gr">Germany</option>
</select>
<div id="sity_id1">
<p>Lion</p>
<p>Paris</p>
</div>
Explanation:
Proper use of the Muenchian grouping method and AVT s.
Proper use of xsl:sort.

With XSLT 1.0 you can use Muechian grouping to perform grouping respectively to identify and eliminate duplicates; the stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:key name="k1" match="data/Object" use="#Id"/>
<xsl:template match="data">
<select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', #Id)[1])]"/>
</select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', #Id)[1]) and key('k1', #Id)[2]]" mode="desc"/>
</xsl:template>
<xsl:template match="data/Object[key('k1', #Id)[2]]">
<option value="{#Id}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object[not(key('k1', #Id)[2])]">
<option value="{Property[#Name = 'WWW']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object" mode="desc">
<div id="city_{#Id}">
<xsl:apply-templates select="key('k1', #Id)/Property[#Name = 'NAME']" mode="desc"/>
</div>
</xsl:template>
<xsl:template match="data/Object/Property" mode="desc">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
transforms the input
<page>
<elements>
<element name="bids">
<data>
<Object Id="id1">
<Property Name="NAME" Order="0">Paris</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1">
<Property Name="NAME" Order="0">Lion</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id2">
<Property Name="NAME" Order="0">Berlin</Property>
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
into
<select>
<option value="id1">France</option>
<option value="http://germany.gr">Germany</option></select><div id="city_id1">
<p>Paris</p>
<p>Lion</p>
</div>
[edit]
Here is an adapted stylesheet that uses a different key to try to implement the changed requirements:
<?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 method="html" indent="yes"/>
<xsl:key name="k1" match="data/Object" use="Property[#Name = 'COUNTRY']"/>
<xsl:template match="data[Object]">
<select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', Property[#Name = 'COUNTRY'])[1])]"/>
</select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', Property[#Name = 'COUNTRY'])[1]) and key('k1', Property[#Name = 'COUNTRY'])[2]]" mode="desc"/>
</xsl:template>
<xsl:template match="data/Object[key('k1', Property[#Name = 'COUNTRY'])[2]]">
<option value="{Property[#Name = 'COUNTRY']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object[not(key('k1', Property[#Name = 'COUNTRY'])[2])]">
<option value="{Property[#Name = 'WWW']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object" mode="desc">
<div id="{Property[#Name = 'COUNTRY']}">
<xsl:apply-templates select="key('k1', Property[#Name = 'COUNTRY'])" mode="link">
<xsl:sort select="#Name"/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="data/Object" mode="link">
<p>
<a href="/index.php?id={#Id}">
<xsl:value-of select="#Name"/>
</a>
</p>
</xsl:template>
</xsl:stylesheet>
When I apply that stylesheet to the input
<?xml version="1.0" encoding="utf-8" ?>
<page>
<elements>
<element>
<data>
<Styles Name="default">
<Style Url="/css.css" Browser="default" Version="default"/>
</Styles>
</data>
</element>
<element type="Digillect.WB.Web.Elements.Site.SiteStructureElement">
<config StartLevel="0" MaxDepth="3" UseItemVisibility="false">
<monikers>
<moniker store="asdasd"/>
</monikers>
</config>
<data ParentPath="/">
</data>
</element>
<element name="bids">
<config>
<Object Id="1b61995a-6e22-4b09-af5f-9a50cdaa7863"/>
<Object Id="baa1d3df-0510-4f68-8a41-1b9b22587134"/>
</config>
<data>
<Object Id="id2" Name="Paris">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1" Name="Lion">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id3" Name="Berlin">
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
the result is
<select>
<option value="France">France</option>
<option value="http://germany.gr">Germany</option></select><div id="France">
<p>Lion</p>
<p>Paris</p>
</div>
so the option elements are grouped as required (although I couldn't figure out what determines the sort order) and the p elements contain links with the #Id value incorporated.

Related

Renaming xml attribute value if another attribute has a specific value using XSLT

I am trying to transform my input xml to output xml using XSLT based on certain conditions.
My input xml :
<top>
<type>Random value</type>
<attribute name="a"/>
<attribute name="b"/>
...
<attribute name="n"/>
<attribute name="some value">
<mapResponses>
<entryMap name="value1">
<section>
<type>STATUS_RESPONSE</type>
<attribute name="status_code" value="1001" />
<attribute name="status_detail" value="OK" />
<attribute name="type" value="Row Line" />
<attribute name="abc" value="def" />
...
<attribute name="hju" value="tyu" />
</section>
</entryMap>
<entryMap name="value2">
<section>
<type>STATUS_RESPONSE</type>
<attribute name="status_code" value="1001" />
<attribute name="status_detail" value="OK" />
<attribute name="type" value="Column value" />
<attribute name="mno" value="pqr" />
...
<attribute name="olp" value="tre" />
</section>
</entryMap>
<entryMap name="value3">
<section>
<type>STATUS_RESPONSE</type>
<attribute name="status_code" value="1001" />
<attribute name="status_detail" value="OK" />
<attribute name="type" value="Other random value" />
<attribute name="oui" value="fry" />
...
<attribute name="tgy" value="adr" />
</section>
</entryMap>
<entryMap name="value4">
<section>
.....
</section>
</entryMap>
<entryMap name="...">
<section>
.....
</section>
</entryMap>
</mapResponses>
</attribute>
</top>
to be transformed to this output xml :
<top>
<type>Random value</type>
<attribute name="a"/>
<attribute name="b"/>
...
<attribute name="n"/>
<attribute name="some value">
<mapResponses>
<entryMap name="value1">
<section>
<type>CREATE_ROW</type>
<attribute name="status_code" value="1001" />
<attribute name="status_detail" value="OK" />
<attribute name="type" value="Row Line" />
<attribute name="abc" value="def" />
...
<attribute name="hju" value="tyu" />
</section>
</entryMap>
<entryMap name="value2">
<section>
<type>CREATE_COLUMN</type>
<attribute name="status_code" value="1001" />
<attribute name="status_detail" value="OK" />
<attribute name="type" value="Column value" />
<attribute name="mno" value="pqr" />
...
<attribute name="olp" value="tre" />
</section>
</entryMap>
<entryMap name="value3">
<section>
<type>CREATE_COLUMN</type>
<attribute name="status_code" value="1001" />
<attribute name="status_detail" value="OK" />
<attribute name="type" value="Other random value" />
<attribute name="oui" value="fry" />
...
<attribute name="tgy" value="adr" />
</section>
</entryMap>
<entryMap name="value4">
<section>
.....
</section>
</entryMap>
<entryMap name="...">
<section>
.....
</section>
</entryMap>
</mapResponses>
</attribute>
</top>
So, when I have <attribute name="type" value="Row Line" /> the <type>STATUS_RESPONSE</type> needs to be changed to <type>CREATE_ROW</type>. And when I have <attribute name="type" value="Column value" /> or <attribute name="type" value="Other random value" /> or any <attribute name="type" value="..." /> where value is NOT Row Line, the <type>STATUS_RESPONSE</type> needs to be changed to <type>CREATE_COLUMN</type>.
The ... are mentioned to generalize the xml that it can contain more content but in same levels.
The <attribute name="type" value="..." /> is always at the 3rd position after STATUS_RESPONSE as mentioned in the input and output xml.
The current XSLT I have is this :
<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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="type">
<xsl:for-each select="attribute[#name='type'][#value='Row Line']">
<xsl:copy/>
<xsl:value-of select="CREATE_ROW"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="type">
<xsl:for-each select="attribute[#name='type'][#value!='Row Line']">
<xsl:copy/>
<xsl:value-of select="CREATE_COLUMN"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am not able to get my desired output from this XSLT. I am very new to XSLT and I just started working on it. Please kindly help me out with this. Thank You!
You had two templates both matching type (so last one in the stylesheet would "win"), then you had a for loop that was looking for attribute elements that were children of type (there are none, because they are siblings).
Move the criteria matching the type elements into a predicate, looking for the attribute elements on the following-sibling:: axis, and in the second template, no need to assert that the #value is not equal to 'Row Line' since the other one already matches when it does (and avoids any potential issues with != on sets where most people really mean not(#value='Row Line')).
<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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="type[following-sibling::attribute[#name='type' and #value='Row Line']]">
<xsl:copy>CREATE_ROW</xsl:copy>
</xsl:template>
<xsl:template match="type[following-sibling::attribute[#name='type']]">
<xsl:copy>CREATE_COLUMN</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Grouping XML with child attribute with XSLT1.0

Im now to XSLT and I need to do some grouping with the value of a child. I have objects, that can have multiple properties and one of them (with id=11) is on I need to use as a grouping element. The amount of properties each object can have varies, but they all have the common prop.
Input:
<object id=1>
<properties>
<prop id=10>Name of object 1</prop>
<prop id=11>Group 1</prop>
<prop id=xy>Whatever properties this object has</prop>
</properties>
</object>
<object id=2>
<properties>
<prop id=10>Name of object 2</prop>
<prop id=11>Group 2</prop>
</properties>
</object>
<object id=3>
<properties>
<prop id=10>Name of object 3</prop>
<prop id=11>Group 1</prop>
</properties>
</object>
<object id=4>
<properties>
<prop id=10>Name of object 4</prop>
<prop id=11>Group 3</prop>
</properties>
</object>
Desired output:
<group name='Group 1'>
<object id=1>
<prop id=10>Name of object 1</prop>
<prop id=11>Group 1</prop>
<prop id=xy>Whatever properties this object has</prop>
</object>
<object id=3>
<properties>
<prop id=10>Name of object 3</prop>
<prop id=11>Group 1</prop>
</properties>
</object>
</group>
<group name='Group 2'>
<object id=2>
<properties>
<prop id=10>Name of object 2</prop>
<prop id=11>Group 2</prop>
</properties>
</object>
</group>
<group name='Group 3'>
<object id=4>
<properties>
<prop id=10>Name of object 4</prop>
<prop id=11>Group 3</prop>
</properties>
</object>
</group>
The idea is to have the items grouped by the value of the prop with id 11.
Ive found multiple different code samples, but none of them have had this specific case, and I havent been able to modify them to suit my need.
My version of the xsl that is essentially that same as Michael Vehrs in that it is also using the Muenchian Method but thought I may as well post it to show a subtle alternative. Here I'm using an apply-templates instead of the for-each/call-template. In terms of performance there is probably nothing between them but personally, I prefer to use apply-templates wherever possible as it typically allows for more flexibility.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" encoding="utf-8"/>
<xsl:key name="groups" match="prop[#id='11']" use="text()"/>
<xsl:template match="/">
<xsl:apply-templates select="//prop[generate-id()=generate-id(key('groups', text())[1])]"/>
</xsl:template>
<xsl:template match="prop">
<group name="{text()}">
<xsl:copy-of select="//object[properties/prop[#id='11']=current()/text()]"/>
</group>
</xsl:template>
</xsl:stylesheet>
This is a basic application of the Muenchian Method (which is explained here, for example):
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="prop" match="//prop[#id='11']/text()" use="." />
<xsl:template match="/">
<xsl:for-each select="//prop[#id='11']/text()[generate-id()
= generate-id(key('prop',.)[1])]">
<xsl:call-template name="group">
<xsl:with-param name="groupname" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="group">
<xsl:param name="groupname" />
<group><xsl:attribute name="name"><xsl:value-of select='$groupname'/></xsl:attribute>
<xsl:for-each select="//object[.//prop[#id='11'][text()=$groupname]]" >
<xsl:copy-of select="." />
</xsl:for-each>
</group>
</xsl:template>
</xsl:stylesheet>

Grouping the consecutive elements in XSLT

I am new to XSLT and XML. I have the following XML. I wanted to group the consecutive child elements.
<Root>
<Child No="1" Month="0" Date="13/08/2014" Payment="100">
<Totals/>
</Child>
<Child No="2" Month="1" Date="13/09/2014" Payment="100">
<Totals/>
</Child>
<Child No="3" Month="2" Date="13/10/2014" Payment="200">
<Totals/>
</Child>
<Child No="4" Month="3" Date="13/11/2014" Payment="300">
<Totals/>
</Child>
<Child No="5" Month="4" Date="13/12/2014" Payment="300">
<Totals/>
</Child>
<Child No="6" Month="5" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="7" Month="6" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="8" Month="7" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="9" Month="8" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="10" Month="9" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
</Root>
I need to create a new PP node when Child[n]/Payment <> Child[n-1]/Payment using the XSLT.
And I am expecting the below result,
<PPS>
<PP>
<Ps>
<StartMonth>0</StartMonth>
<EndMonth>1</EndMonth>
<P>
<Amount>100</Amount>
<startP>1</startP>
</P>
<P>
<Amount>100</Amount>
<startP>2</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>2</StartMonth>
<EndMonth>2</EndMonth>
<P>
<Amount>200</Amount>
<startP>3</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>3</StartMonth>
<EndMonth>4</EndMonth>
<P>
<Amount>300</Amount>
<startP>4</startP>
</P>
<P>
<Amount>300</Amount>
<startP>5</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>5</StartMonth>
<EndMonth>9</EndMonth>
<P>
<Amount>100</Amount>
<startP>6</startP>
</P>
<P>
<Amount>100</Amount>
<startP>7</startP>
</P>
<P>
<Amount>100</Amount>
<startP>8</startP>
</P>
<P>
<Amount>100</Amount>
<startP>9</startP>
</P>
<P>
<Amount>100</Amount>
<startP>10</startP>
</P>
</Ps>
</PP>
</PPS>
Here is my sample xslt
<xsl:stylesheet version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="x" match="Child" use="#Payment"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="CF">
<xsl:if test="generate-id(.) = generate-id(key('x',#Payment)[1])">
<PP>
<Ps>
<xsl:for-each select="key('x', #Payment)">
<P>
<Amount><xsl:value-of select="format-number(translate(#Payment, ',','.'),'0.00')"/></Amount>
<startP><xsl:value-of select="#Date"/></startP>
</P>
</xsl:for-each>
</Ps>
</PP>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
</xsl:stylesheet>
This would work:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="Child">
<xsl:variable name="Payment" select="#Payment"/>
<xsl:if test="not(preceding::*[1][self::Child and #Payment = $Payment])">
<PPS>
<Ps>
<xsl:apply-templates select="current()"/>
<xsl:apply-templates select="following::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</Ps>
</PPS>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
<xsl:template match="Child">
<xsl:param name="Payment"/>
<P>
<Amount>
<xsl:value-of select="#Payment"/>
</Amount>
<startP>
<xsl:value-of select="#No"/>
</startP>
</P>
<xsl:apply-templates select="following::*[1][self::Child and #Payment = $Payment]"/>
</xsl:template>
</xsl:stylesheet>

Create list of nodes by copying from another lists

I have a XSLT template where I need to merge two lists of nodes conditionally. I have the following two XML fragments:
<vo>
<field name="userLoginName" nameLDAP="uid" type="String"/>
<field name="displayName" nameLDAP="displayName" type="String"/>
<field name="firstName" nameLDAP="givenName" type="String"/>
<field name="lastName" nameLDAP="sn" type="String"/>
<field name="mail" nameLDAP="mail" type="String"/>
<field name="userPassword" nameLDAP="userPassword" type="String" hidden="true"/>
<field name="center" nameLDAP="center" type="String"/>
</vo>
<input>
<field name="userPassword"/>
<field name="oldPasswordInQuotes" nameLDAP="unicodePwd" type="byte[]"/>
</input>
I want to create a xml fragment that will have the same nodeas as input/field. For each one of those nodes I want to check if there is a node with the same name in the list vo/field. If there is, then it should be copied to the new list. Otherwise, it should copy the same node we are iterating over. In this case the output should be something like this:
<field name="userPassword" nameLDAP="userPassword" type="String" hidden="true"/>
<field name="oldPasswordInQuotes" nameLDAP="unicodePwd" type="String"/>
So far I've the following transformation:
<xsl:variable name="fields" select="vo/field" />
<xsl:variable name='allParameters'>
<xsl:for-each select="input/field">
<xsl:variable name="inputFieldName" select="#name"/>
<xsl:choose>
<xsl:when test="$fields[#name = $inputFieldName]">
<xsl:copy-of select="$fields[#name = $inputFieldName]"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:message>Parameters <xsl:copy-of select="$allParameters" /> </xsl:message>
<xsl:for-each select="$allParameters">
<xsl:message>Parameter <xsl:value-of select="#name" /> found !!! </xsl:message>
</xsl:for-each>
The output is
Parameters <field name="userPassword" nameLDAP="userPassword" type="String" hidden="true"/><field name="oldPasswordInQuotes" nameLDAP="unicodePwd" type="byte[]"/>
Parameter found !!!
The first xsl:message seems to show that the copy worked fine, but when I try to iterate over it, it is obviously not working(there is only one message "Parameter found" and it is not showing the parameter name). What am I missing?
I think you want something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="vo" match="vo/field" use="#name" />
<xsl:template match="/">
<xsl:for-each select="root/input/field">
<xsl:choose>
<xsl:when test="key('vo', #name)">
<xsl:copy-of select="key('vo', #name)"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Applied to a wel-formed input:
<root>
<vo>
<field name="userLoginName" nameLDAP="uid" type="String"/>
<field name="displayName" nameLDAP="displayName" type="String"/>
<field name="firstName" nameLDAP="givenName" type="String"/>
<field name="lastName" nameLDAP="sn" type="String"/>
<field name="mail" nameLDAP="mail" type="String"/>
<field name="userPassword" nameLDAP="userPassword" type="String" hidden="true"/>
<field name="center" nameLDAP="center" type="String"/>
</vo>
<input>
<field name="userPassword"/>
<field name="oldPasswordInQuotes" nameLDAP="unicodePwd" type="byte[]"/>
</input>
</root>
results in:
<field name="userPassword" nameLDAP="userPassword" type="String" hidden="true"/>
<field name="oldPasswordInQuotes" nameLDAP="unicodePwd" type="byte[]"/>
Edit:
To answer your question regarding the number of messages in your test: there is only one $allParameters node; to iterate over its inner nodes (assuming XSLT 2.0), try:
<xsl:for-each select="$allParameters/field">
<!-- your code here -->
</xsl:for-each>

Ordering nodes in XSL Transformation

Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<BigData version="2.1" xmlns="bank.xsd">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="BANK" distName="CITY-1/ABC-1/BANK-1" operation="create" timeStamp="2013-07-27T15:48:20"/>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="update" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Branch" distName="CITY-1/ABC-1/BANK-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-2" operation="update" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
Transformation XSL :
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:x="bank.xsd" exclude-result-prefixes="x">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:object[#class = 'BANK' ]">
</xsl:template>
<xsl:template match="x:object[#class = 'Branch' ]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:object[#class = 'BranchItem' ]">
<xsl:variable name="branchItem" select="."/>
<xsl:choose>
<xsl:when test="$branchItem/#operation='update' and not(contains($branchItem/#distName, 'JOBS_CREATED_USING_NE_LOCAL_UI'))">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">delete</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="x:object[#class = 'BranchItemPeriod' ]">
<xsl:variable name="branchPeiod" select="."/>
<xsl:choose>
<xsl:when test="$branchPeiod/#operation='update' and not(contains($branchPeiod/#distName, 'JOBS_CREATED_USING_NE_LOCAL_UI'))">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output XML :
<?xml version="1.0" encoding="UTF-8"?>
<BigData xmlns="bank.xsd" version="2.1">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Branch" distName="CITY-1/ABC-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="delete" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
Desired OUTPUT XML:
<?xml version="1.0" encoding="UTF-8"?>
<BigData xmlns="bank.xsd" version="2.1">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="Branch" distName="CITY-1/ABC-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="delete" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
I could achieve most of desired output except for few...
I want the output to be sorted based on distName attribute of object nodes.
I want the sort to happen only to certain child nodes with class names as Branch , BranchItem , BranchItemPeriod.
Here i try for update with delete and create operation, so i want also to maintain the order of delete and create too which i do in present transformation logic or else can it so happen that i sort all first based on above criteria and apply the other transformation logic.
Any suggestion or help is highly appreciated.
I think what you need here is a template that matches the InsideData element, where you can then select the child object elements in the order you require.
You would first start by outputting the non-"object" elements, assuming these always come before the object elements.
<xsl:apply-templates select="#*|node()[not(self::x:object)]"/>
Then you would select the object elements with the class attribute you desire, sorting in the order you require too:
<xsl:apply-templates select="x:object[#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod']">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
Finally, you would output the object elements which have the the class attributes.
<xsl:apply-templates select="x:object[not(#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod')]"/>
Try adding this template to your XSLT to see how you get on:
<xsl:template match="x:InsideData">
<xsl:copy>
<xsl:apply-templates select="#*|node()[not(self::x:object)]"/>
<xsl:apply-templates select="x:object[#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod']">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
<xsl:apply-templates select="x:object[not(#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod')]"/>
</xsl:copy>
</xsl:template>