xml to xml using xslt : Recursive matching & creating hierarchy like tree - xslt

I am completely new to xslt. Please help me to write style sheet.I have input xml like this
Input XML:
<elements>
<e1>
<pid>1</pid>
<cid>2</cid>
</e1>
<e1>
<pid>1</pid>
<cid>3</cid>
</e1>
<e1>
<pid>2</pid>
<cid>4</cid>
</e1>
</elements>
Desired XML:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data></data>
</unit>
<data></data>
</unit>
<unit id="3">
<data></data>
</unit>
<data></data>
</unit>
</tree>
I feel this should be really easy but I'm struggling to find information about how to do this. My XSLT knowledge isn't great.

I'm not 100% sure how you want the XSLT to determine from that input that the top id is 1 (is it because it's the only pid value with no corresponding cid values, or is it always 1?). Nonetheless, this should do the job:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByC" match="e1" use="cid" />
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<!-- This will be the value of the <pid> that has no <cid> references to
it (assuming there is only one top-level <pid>) -->
<xsl:with-param name="id"
select="string(/elements/e1/pid[not(key('kItemsByC', .))])" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
When this is run on your sample input, this produces:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data />
</unit>
<data />
</unit>
<unit id="3">
<data />
</unit>
<data />
</unit>
</tree>
Note: The above XSLT has logic to attempt to dynamically locate the top-level ID. If it can be assumed that the top-level unit will always have ID 1, then one key and the above XSLT's (somewhat) complicated formula can be eliminated:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<xsl:with-param name="id" select="1" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
This also produces the requested output when run on your sample input.

Ah, after reading JLRishe I think I get it: "pid" means "parent ID", "cid" means "child ID", and e1 represents a parent-child relationship. Brilliant detective work, I would never have worked that out for myself.
The basic model is that when you are positioned on a parent element you do apply-templates to its children. This applies just as well if the parent/child relationships are represented by primary/foreign keys as when they are represented using the XML hierarchy. So the essence is:
<xsl:template match="e1">
<unit id="{pid}">
<xsl:apply-templates select="//e1[pid=current()/cid]"/>
<data/>
</unit>
</xsl:template>
which is essentially JLRishe's solution except he has added an optimization using keys.

Related

XPath Predicate "is parent the current node?"

I have a node set and I'd like to use a predicate to select from it only those nodes which have a template's current node as their parent.
I'm using XSL 1.0 and msxsl.
In the following non-working code, the predicate [parent::current()] does not work as I want it to as current() is not a valid NodeTest:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:variable name="set" select="//node[#attr1 = 'bbb']"/>
<xsl:template name="TemplateA">
<xsl:for-each select="$set[parent::current()]">
<result-node>
<xsl:value-of select="#value"/>
<xsl:text>A</xsl:text>
</result-node>
</xsl:for-each>
</xsl:template>
<xsl:template name="TemplateB">
<xsl:for-each select="$set[parent::current()]">
<result-node>
<xsl:value-of select="#value"/>
<xsl:text>B</xsl:text>
</result-node>
</xsl:for-each>
</xsl:template>
<xsl:template match="/body">
<result>
<xsl:for-each select="parentNode">
<xsl:call-template name="TemplateA" />
<xsl:call-template name="TemplateB" />
</xsl:for-each>
</result>
</xsl:template>
</xsl:transform>
Here is a sample XML to feed the above:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<parentNode>
<node attr1="aaa" value="1" />
<node attr1="bbb" value="2" />
</parentNode>
<parentNode>
<node attr1="aaa" value="3" />
<node attr1="bbb" value="4" />
</parentNode>
</body>
Here is the desired output:
<result>
<result-node>2A</result-node>
<result-node>2B</result-node>
<result-node>4A</result-node>
<result-node>4B</result-node>
</result>
The above example can also be found here: http://xsltransform.net/jyH9rMg
The way to compare nodes for identity in XSLT 1.0 is to use generate-id():
<xsl:for-each select="$set[generate-id(..) = generate-id(current())]">
.. is a shorthand for parent::node() and refers to the parent of the context node (the node being tested by this execution of the predicate).

Transformation of text file into an xml using datapower

I want to transfer a non-xml text file delimited by '|' characters into an xml using Datapower.
Following is file (sample1)
10|20003|24/23/25|23890
Now i have to break this into the following XML
<ResponseType>
<ResCode>10</ResCode>
<Id>20003</Id>
<SoftCode>24/23/25</SoftCode>
<StatusCode>23890</StatusCode>
</ResponseType>
What I did was following--
1>Create a Transform action in the service that will be receiving non-XML requests.
2>Select "Use XSLT specified in this action on a non-XML message" to specify that this is a Binary Transform.
3>Upload the following stylesheet as the Processing Control File.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
version="1.0">
<dp:input-mapping href="sample1.ffd" type="ffd"/>
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:copy-of select="ResponseType"/>
<xsl:call-template name="str:tokenize">
<xsl:with-param name="string" select="string" />
</xsl:call-template>
</xsl:template>
<xsl:template name="str:tokenize">
<xsl:with-param name="string" select="">
str:tokenize('string', '|')
</xsl:with param>
</xsl:template>
</xsl:stylesheet>
and here is my sample1.ffd(which I have uploaded in my local:// directory in Datapower
<File name="ResponseType">
<!-- capture all data into this tag -->
<Field name="ResCode/Id/SoftCode/StatusCode" />
</File>
But I am not getting desired output , I think my xslt is quite wrong
What can I do do to get desired output?
In DataPower using FFD the following should work:
1) Add the FFD file (below one of my old education samples):
<File name="CSVFILE">
<Group name="CSVLine" minOccurs="0" maxOccurs="unbounded" delim="\n">
<Field name="id"/>
<Field name="fname" delim=","/>
<Field name="lname" delim=","/>
<Field name="title" delim=","/>
<Field name="dept" delim=","/>
<Field name="org"/>
</Group>
</File>
2) Add the XSLT (this one simply copies the FFD transformed XML to output):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
version="1.0">
<dp:input-mapping href="CSVFILE.FFD" type="ffd"/>
<!-- This stylesheet copies the input to the output -->
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
3) Send a message:
1,Anders,Wasen,B2B Architect,DataPower Dev,Enfo Zystems
2,Jean-Luc,Piccard,Captain,USS Enterprise,Star Fleet
4) This will result in the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<CSVFILE>
<CSVLine>
<id>1</id>
<fname>Anders</fname>
<lname>Wasen</lname>
<title>B2B Architect</title>
<dept>DataPower Dev,Enfo Zystems</dept>
<org/>
</CSVLine>
<CSVLine>
<id>2</id>
<fname>Jean-Luc</fname>
<lname>Piccard</lname>
<title>Captain</title>
<dept>USS Enterprise,Star Fleet</dept>
<org/>
</CSVLine>
</CSVFILE>
Make sure that you change the XSLT Transform action into "Transform Binary" and set Request Type to "non-xml", else it will not work!
Hope this will help you! :)
I'm not sure how IBM Datapower might solve this problem, but for XSLT, you would at least wrap your input in a XML element:
<Whatever>
10|20003|24/23/25|23890
</Whatever>
And then you could go on with a transformation like follows. The hard part is splitting your text input. In XSLT 1.0, there is no function available for that, so you need a recursive template.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" version="1.0" exclude-result-prefixes="msxml">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:variable name="tokenized">
<items>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="//text()" />
</xsl:call-template>
</items>
</xsl:variable>
<ResponseType>
<ResCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[1]/text()" />
</ResCode>
<Id>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[2]/text()" />
</Id>
<SoftCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[3]/text()" />
</SoftCode>
<StatusCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[4]/text()" />
</StatusCode>
</ResponseType>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="string" />
<xsl:variable name="item" select="normalize-space( substring-before( concat( $string, '|'), '|'))" />
<xsl:if test="$item">
<item>
<xsl:value-of select="$item" />
</item>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="substring-after($string,'|')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Get distinct values from xml

My sample xml looks below: I need to get the distinct states from xml. I am using xslt 1.0 in vs 2010 editor.
<?xml version="1.0" encoding="utf-8" ?>
<states>
<node>
<value>2</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>NJ</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
</states>
My xslt looks like below:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns:user="urn:my-scripts">
<xsl:output method="text" indent="yes"/>
<xsl:key name="st" match="//states/node/state" use="." />
<xsl:variable name="disst">
<xsl:for-each select="//states/node[contains(value,1)]/state[generate-id()=generate-id(key('st',.)[1])]" >
<xsl:choose>
<xsl:when test="(position() != 1)">
<xsl:value-of select="concat(', ',.)" disable-output-escaping="yes"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/" >
<xsl:value-of disable-output-escaping="yes" select="$disst"/>
</xsl:template>
</xsl:stylesheet>
Output: DE,NJ,NY
My above xml looks good for the above test xml.
If I change the xml as below:
<?xml version="1.0" encoding="utf-8" ?>
<states>
<node>
<value>2</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>NJ</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
</states>
It in not picking the state DE. Can any one suggest the suitable solution.Thanks in advance.
I need to find out the distinct states from the xml.
The problem here is your use of a predicate in your Muenchian grouping XPath:
[contains(value,1)]
This will often make Muenchian grouping fail to find all of the available distinct values. Instead, you should add the predicate to the key:
<xsl:key name="st" match="//states/node[contains(value, 1)]/state" use="." />
Alternatively, you can apply the predicate inside the grouping statement:
<xsl:apply-templates
select="//states/node
/state[generate-id() =
generate-id(key('st',.)[contains(../value, 1)][1])]" />
Full XSLT (with some improvements):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:user="urn:my-scripts">
<xsl:output method="text" indent="yes"/>
<xsl:key name="st" match="//states/node/state" use="." />
<xsl:variable name="a" select="1" />
<xsl:variable name="disst">
<xsl:apply-templates
select="//states/node
/state[generate-id() =
generate-id(key('st',.)[contains(../value, $a)][1])]" />
</xsl:variable>
<xsl:template match="state">
<xsl:if test="position() > 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select ="." disable-output-escaping="yes" />
</xsl:template>
<xsl:template match="/" >
<xsl:value-of disable-output-escaping="yes" select="$disst"/>
</xsl:template>
</xsl:stylesheet>
Result when run on your sample XML:
DE,NJ,NY

Map empty to a node with xslt

I am using "Altova MapForce" mapping tool and I'm mapping node to node. The resulted xml (Using the altova xslt processor) is without nodes that are not empty. The resulted xml (Using the biztalk xslt processor, with the generated xsl from the Altova Mapforce) is with nodes that are not empty. That nodes are empty on the source.
My Goal is to connect the source node (Using Altova MapForce mapping tool) to a custom written XSLT, and then to connect it to the target.
Here is my code: in.xml - Instance input for executing the map (Please notice the empty id tag: )
<ns0:Root xmlns:ns0="http://BizTalk_Server_Project1.Schema1">
<id root="root_0" extension="extension_1" />
</ns0:Root>
Schema1.xsd - source and target schema
<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns="http://BizTalk_Server_Project1.Schema1" xmlns:b="http://schemas.microsoft.com/BizTalk/2003" targetNamespace="http://BizTalk_Server_Project1.Schema1" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Root">
<xs:complexType>
<xs:sequence>
<xs:element name="id">
<xs:complexType>
<xs:attribute name="root" type="xs:string" />
<xs:attribute name="extension" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
New.mfd - Altova mapping file
<?xml version="1.0" encoding="UTF-8"?>
<mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="19">
<component name="defaultmap1" blackbox="0" uid="1" editable="1">
<properties SelectedLanguage="xslt"/>
<structure>
<children>
<component name="document" library="xml" uid="4" kind="14">
<properties/>
<view rbx="150" rby="200"/>
<data>
<root>
<header>
<namespaces>
<namespace/>
<namespace uid="http://BizTalk_Server_Project1.Schema1"/>
<namespace uid="http://www.altova.com/mapforce"/>
</namespaces>
</header>
<entry name="FileInstance" ns="2" expanded="1">
<entry name="document" ns="2" expanded="1" casttotargettypemode="cast-in-subtree">
<entry name="Root" ns="1" expanded="1">
<entry name="id" expanded="1">
<entry name="extension" type="attribute" outkey="4"/>
</entry>
</entry>
</entry>
</entry>
</root>
<document schema="Schema1.xsd" outputinstance="Schema1.xml" instanceroot="{http://BizTalk_Server_Project1.Schema1}Root"/>
<wsdl/>
</data>
</component>
<component name="document" library="xml" uid="5" kind="14">
<properties XSLTDefaultOutput="1"/>
<view ltx="593" rbx="743" rby="200"/>
<data>
<root>
<header>
<namespaces>
<namespace/>
<namespace uid="http://BizTalk_Server_Project1.Schema1"/>
<namespace uid="http://www.altova.com/mapforce"/>
</namespaces>
</header>
<entry name="FileInstance" ns="2" expanded="1">
<entry name="document" ns="2" expanded="1" casttotargettypemode="cast-in-subtree">
<entry name="Root" ns="1" expanded="1">
<entry name="id" expanded="1">
<entry name="extension" type="attribute" inpkey="5"/>
</entry>
</entry>
</entry>
</entry>
</root>
<document schema="Schema1.xsd" outputinstance="Schema1.xml" instanceroot="{http://BizTalk_Server_Project1.Schema1}Root"/>
<wsdl/>
</data>
</component>
</children>
<graph directed="1">
<edges/>
<vertices>
<vertex vertexkey="4">
<edges>
<edge vertexkey="5" edgekey="6"/>
</edges>
</vertex>
</vertices>
</graph>
</structure>
</component>
</mapping>
Xsl.xsl - Xsl generated by the Altova Mapper
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://BizTalk_Server_Project1.Schema1" xmlns:agt="http://www.altova.com/Mapforce/agt" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 agt xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template name="agt:var2_MapToSchema1_function">
<xsl:param name="par0"/>
<xsl:attribute name="extension">
<xsl:value-of select="string($par0/#extension)"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="/">
<Root xmlns="http://BizTalk_Server_Project1.Schema1">
<xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance">http://BizTalk_Server_Project1.Schema1 C:/Users/OhadAv/Desktop/ForAltova/Schema1.xsd</xsl:attribute>
<id xmlns="">
<xsl:for-each select="ns0:Root/id">
<xsl:variable name="var1_extension">
<xsl:if test="#extension">
<xsl:value-of select="'1'"/>
</xsl:if>
</xsl:variable>
<xsl:if test="string(boolean(string($var1_extension))) != 'false'">
<xsl:call-template name="agt:var2_MapToSchema1_function">
<xsl:with-param name="par0" select="."/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</id>
</Root>
</xsl:template>
</xsl:stylesheet>
GeneratedByBizTalkMapperAfterXSLTmap.xml - Output instance of the BizTalk mapper after using the "Xsl.xsl" for the map (Please notice the non-empty id tag that has been generated when editing the xml file:
<?xml version="1.0" encoding="utf-8"?>
<Root xsi:schemaLocation="http://BizTalk_Server_Project1.Schema1 C:/Users/OhadAv/Desktop/ForAltova/Schema1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://BizTalk_Server_Project1.Schema1">
<id extension="extension_1" xmlns="">
</id>
</Root>
The xslt generated by the Altova mapper looks pretty messy.
e.g. use of a one time only variable, a call template just used once, and repeated conversion like string(boolean(string(
I'm not 100% clear on what mapping you need to do in your map.
If you want to remove the root attribute entirely, and provide an id element even if it is missing, then the below xslt will work:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://BizTalk_Server_Project1.Schema1"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="ns0 xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/ns0:Root">
<Root xmlns="http://BizTalk_Server_Project1.Schema1">
<xsl:element name="id">
<xsl:for-each select="ns0:Root/id">
<xsl:if test="#extension">
<xsl:attribute name="extension">
<xsl:value-of select="string(#extension)"/>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
</xsl:element>
</Root>
</xsl:template>
</xsl:stylesheet>
This produces XML without the xmlns, extraneous schema locations, etc like so:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns="http://BizTalk_Server_Project1.Schema1">
<id extension="extension_1" />
</Root>
If however you mean that you want to omit the id element entirely if it isn't present on the input node, then change the template as follows.
<xsl:template match="/ns0:Root">
<Root xmlns="http://BizTalk_Server_Project1.Schema1">
<xsl:if test="id">
<xsl:element name="id">
<xsl:attribute name="extension">
<xsl:value-of select="string(id/#extension)"/>
</xsl:attribute>
</xsl:element>
</xsl:if>
</Root>
</xsl:template>
Which Maps
<ns0:Root xmlns:ns0="http://BizTalk_Server_Project1.Schema1">
</ns0:Root>
To:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns="http://BizTalk_Server_Project1.Schema1" />

xpath get matches in sublist

I've got the following xml:
<vo class="GroupEntry" buildByAlias="true">
<objectClass name="groupOfNames"/>
<field name="commonName" nameLDAP="cn" type="String"/>
<field name="descriptione" nameLDAP="description" type="String"/>
<field name="member" nameLDAP="member" type="String[]"/>
</vo>
<update method="addMember" modificationMode="ADD_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
I'm using XSLT to transform it, and I'm need, for each update, to get the fields in the vo that correspond to the field defined in the input. It would be something like this:
<xsl:variable name="fields" select="vo/field" />
<xsl:for-each select="update">
<xsl:variable name='fieldsForInput' select = "$fields[#name]=input/fields[#name]"/>
<xsl:for-each select="$fieldsForInput">
<xsl:value-of select="#type"/> <xsl:value-of select="#name"/>
<xsl:for-each>
</xsl:for-each>
But it doesn't found anything. Any ideas?
Thanks
JL
From the shown fragments it's difficult helping you and understadning what you want. However your case seems perfect for using xsl:key.
For example, if you create a key at the beginning of the transform like this:
<xsl:key name="fields" match="vo/field" use="#name"/>
You can use it inside your matching template as follows:
<xsl:for-each select="update/input">
<xsl:copy-of select="key('fields',current()/field/#name)"/>
</xsl:for-each>
I would not use a xsl:foreach anyway. But it's hard to give you a complete solution if you provide only fragments. Also is not clear if you want just match or replace field.
Example showing how to get the field name/type for each update/input/field.
XSLT 1.0 tested with Saxon 6.5.5
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="fields" match="vo/field" use="#name"/>
<xsl:template match="/root">
<xsl:apply-templates select="update"/>
</xsl:template>
<xsl:template match="update">
<xsl:value-of select="concat('-',#method,'
')"/>
<xsl:apply-templates select="input/field"/>
</xsl:template>
<xsl:template match="input/field">
<xsl:value-of select="concat('--',#name,' ',key('fields',#name)/#type,'
')"/>
</xsl:template>
</xsl:stylesheet>
Applied on:
<root>
<vo class="GroupEntry" buildByAlias="true">
<objectClass name="groupOfNames"/>
<field name="commonName" nameLDAP="cn" type="String"/>
<field name="description" nameLDAP="description" type="String"/>
<field name="member" nameLDAP="member" type="String[]"/>
</vo>
<update method="addMember" modificationMode="ADD_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
<update method="deleteMember" modificationMode="DELETE_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
</root>
Produces:
-addMember
--member String[]
--description String
-deleteMember
--member String[]
--description String
Two solutions:
Solution1 (no keys):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match=
"vo/field[#name=../../update/*/*/#name]">
<xsl:value-of select="concat(#name,' ',#type,'
')"/>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document (corrected to be made well-formed):
<t>
<vo class="GroupEntry" buildByAlias="true">
<objectClass name="groupOfNames"/>
<field name="commonName" nameLDAP="cn" type="String"/>
<field name="description" nameLDAP="description" type="String"/>
<field name="member" nameLDAP="member" type="String[]"/>
</vo>
<update method="addMember" modificationMode="ADD_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
</t>
the wanted, correct result is produced:
description String
member String[]
Solution2 (using a key):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kFieldByName" match="vo/field"
use="#name"/>
<xsl:template match="/*">
<xsl:apply-templates mode="selected" select=
"key('kFieldByName', update/*/*/#name)"/>
</xsl:template>
<xsl:template match="vo/field" mode="selected">
<xsl:value-of select="concat(#name,' ',#type,'
')"/>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document (above), the same correct result is produced:
description String
member String[]