Gathering xml fields in respective output header field tags - xslt

I have a requirement to gather same "type" and "pin" field values under one field in output when the values are present in both fields else create under seperate field.
this is my input to xslt:
<?xml version='1.0' encoding='UTF-8'?>
<record>
<file>
<type>DI</type>
<pin>123</pin>
</file>
<file>
<type>DI</type>
<pin>123</pin>
</file>
<file>
<type>DI</type>
<pin>456</pin>
</file>
<file>
<type>DI</type>
<pin></pin>
</file>
<file>
<type>DI</type>
<pin></pin>
</file>
<file>
<type>DM</type>
<pin>123</pin>
</file>
<file>
<type>DM</type>
<pin>123</pin>
</file>
<file>
<type>DM</type>
<pin>456</pin>
</file>
<file>
<type>DM</type>
<pin></pin>
</file>
<file>
<type>DM</type>
<pin></pin>
</file>
<file>
<type>CM</type>
<pin>123</pin>
</file>
<file>
<type>CM</type>
<pin>123</pin>
</file>
<file>
<type>CM</type>
<pin>456</pin>
</file>
<file>
<type>CM</type>
<pin></pin>
</file>
<file>
<type>CM</type>
<pin></pin>
</file>
</record>
i want the above payload converted to below expected output
<?xml version='1.0' encoding='UTF-8'?>
<root>
<record>
<cat>
<file>
<type>DI</type>
<pin>123</pin>
</file>
<file>
<type>DI</type>
<pin>123</pin>
</file>
</cat>
<cat>
<file>
<type>DM</type>
<pin>123</pin>
</file>
<file>
<type>DM</type>
<pin>123</pin>
</file>
</cat>
<cat>
<file>
<type>CM</type>
<pin>123</pin>
</file>
<file>
<type>CM</type>
<pin>123</pin>
</file>
</cat>
<cat>
<file>
<type>DI</type>
<pin>456</pin>
</file>
</cat>
<cat>
<file>
<type>DM</type>
<pin>456</pin>
</file>
</cat>
<cat>
<file>
<type>CM</type>
<pin>456</pin>
</file>
</cat>
<cat>
<file>
<type>DI</type>
<pin></pin>
</file>
</cat>
<cat>
<file>
<type>DI</type>
<pin></pin>
</file>
</cat>
<cat>
<file>
<type>DM</type>
<pin></pin>
</file>
</cat>
<cat>
<file>
<type>DM</type>
<pin></pin>
</file>
</cat>
<cat>
<file>
<type>CM</type>
<pin></pin>
</file>
</cat>
<cat>
<file>
<type>CM</type>
<pin></pin>
</file>
</cat>
</record>
</root>
my xslt is not working
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/record">
<root><xsl:for-each-group select="record" group-by="type">
<xsl:choose>
<xsl:for-each-group select="record" group-by="pin">
<xsl:when test="current-grouping-key()">
<cat>
<xsl:copy-of select="current-group()"/>
</cat>
</xsl:when></xsl:for-each-group>
<xsl:otherwise>
<cat>
<xsl:copy-of select="."/>
</cat>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group></root>
</xsl:template>
</xsl:stylesheet>
The xslt should be transformed to generate above output based on incoming payload "type" and "pin".
example:
In above case, if "type"="DI" then check in incoming xml for where both "type" and "pin" field values are same, then merge them in one target field with same details, but if "type" is available and "pin" value is blank then they should create under separate target field individually.
"type" is mandatory field and "pin" is optional field.

I think, if you adapt your code to
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<root>
<xsl:for-each-group select="record/file" group-by="type">
<xsl:for-each-group select="current-group()" group-by="pin">
<xsl:choose>
<xsl:when test="normalize-space(current-grouping-key())">
<cat>
<xsl:copy-of select="current-group()"/>
</cat>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="current-group()">
<cat>
<xsl:copy-of select="."/>
</cat>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
your requirement is implemented.

Related

I need to change an xml attribute value conditionaly from an external xml using Xsl

I have the following 2 xml files,
Mapping.xml
<xml>
<map ordinal="0" reverse="xxx" forward="ThisIsXxx" />
<map ordinal="0" reverse="yyy" forward="ThisIsYyy" />
<map ordinal="0" reverse="zzz" forward="thisIsZzz" />
<map ordinal="0" reverse="xx1" forward="ThisIsXx1Info" />
<map ordinal="0" reverse="yy1" forward="ThisIsYy1Info" />
<map ordinal="0" reverse="zz1" forward="ThisIsZz1Info" />
</xml>
and an input xml file with a series of elements with some of them having a 'name' attribute
second.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<Children>
<child name="xxx">
<info name="xx1">
</info>
</child>
<child name="yyy">
<info name="yy1">
</info>
</child>
<child name="zzz">
<info name="zz1">
</info>
</child>
</Children> <!-- Added by edit -->
</xml>
What I need is if direction is 'forward' then the second.xml files #name to be set to the value from #forward from the Mapping.xml such as the following,
output.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<Children>
<child name="ThisIsXxx">
<info name="ThisIsXx1Info">
</info>
</child>
<child name="ThisIsZzz">
<info name="ThisIsYy1Info">
</info>
</child>
<child name="ThisIsYyy">
<info name="ThisIsZz1Info">
</info>
</child>
</xml>
Xslt file I have is setting all #name="" but not getting any values from the external file..
<?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">
<xsl:output method="xml" indent="yes"/>
<!-- get the mapping db -->
<xsl:variable name="mapDb" select="document('Mapping.xml')" />
<xsl:variable name="mapFwd" select="forward" />
<!-- for all #name find the #mapdb/*/#reverse == #name and set #name=#mapDb/*/forward -->
<xsl:template match="*/#name">
<xsl:choose>
<xsl:when test="$mapFwd='forward'">
<xsl:attribute name="name">
<xsl:value-of select="$mapDb/xml/map[#reverse='{#name}']/#forward"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="name">
<xsl:value-of select="$mapDb/xml/map[#forward='{#name}']/#reverse"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--Copy Everything unchanged-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would appreciate some help.
I think you want to use current() e.g. $mapDb/xml/map[#reverse=current()] in your comparisons in the template matching the #name attribute and of course the variable should be initialized with <xsl:variable name="mapFwd" select="'forward'"/>.

XSLT output is not correct

I am trying to figure out what is wrong with my xsl file. Here is my sample xml file:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="user#host" start="2015-12-30T14:55:40.156Z">
<ls>
<destination value="/test/folder" />
<files>
<file>
<filename value="folder1" />
<type value="d" />
<modification value="2015-03-04T21:09:25.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="folder2" />
<type value="d" />
<modification value="2015-03-04T21:08:47.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="folder3" />
<type value="d" />
<modification value="2015-03-04T21:08:57.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="folder4" />
<type value="d" />
<modification value="2015-03-04T21:09:14.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="file1.txt" />
<type value="-" />
<size value="113" />
<modification value="2015-12-30T14:55:07.000Z" />
<permissions value="rw-rw-rw-" />
</file>
</files>
<result success="true" />
</ls>
</session>
This is my xsl file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>filename,filedatetime,filetype</xsl:text>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="file"/>
<xsl:text>-eof-</xsl:text>
</xsl:template>
<xsl:template match="file">
<xsl:text>"</xsl:text>
<xsl:value-of select="filename/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="modification/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="type/#value" />
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
The output that I am getting is:
filename,filedatetime,filetype
-eof-
I cannot seem to figure out how to get the values of "filename", "modification" and "type" elements. Any help would be appreciated.
The line <xsl:apply-templates select="file"/> will only match immediate children of the root node of type file and there aren't any of those.
If you want all file children at any depth you could use <xsl:apply-templates select="//file"/> instead.
The XML has a default namespace, so the elements in your XSL won't match unless their namespaces match too.
The following seems to work (note the definition and use of the xx namespace):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xx="http://winscp.net/schema/session/1.0">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>filename,filedatetime,filetype</xsl:text>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="//xx:file" />
<xsl:text>-eof-</xsl:text>
</xsl:template>
<xsl:template match="xx:file">
<xsl:text>"</xsl:text>
<xsl:value-of select="xx:filename/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="xx:modification/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="xx:type/#value" />
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Grouping with xslt and child nodes

I have tried some of the examples here, but I still can't get the correct output. I haven't worked much with XSLT before. I want to group on the "Detail" element and get all the "Data" elements matching that group as children to the "Detail" element.
Example:
input
<?xml version="1.0" encoding="utf-8"?>
<File>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
<Detail type="A" group="1">
<Data>
<Nr>6</Nr>
</Data>
</Detail>
</File>
Wanted output ("Detail type="A" group="1"> elements grouped together and all the elements matching that group as children)
<?xml version="1.0" encoding="utf-8"?>
<File>
<Detail type="A" group="1" >
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
<Data>
<Nr>6</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
</File>
Thanks for help :)
I. XSLT 1.0
Here's a solution that makes use of push-oriented templates, rather than <xsl:for-each> (which is normally a more reusable approach).
When this XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="kDetailAttr" match="Detail" use="concat(#type, '+', #group)" />
<xsl:template match="/*">
<File>
<xsl:apply-templates select="Detail[generate-id() = generate-id(key('kDetailAttr', concat(#type, '+', #group))[1])]" />
</File>
</xsl:template>
<xsl:template match="Detail">
<Detail type="{#type}" group="{#group}">
<xsl:copy-of select="key('kDetailAttr', concat(#type, '+', #group))/Data" />
</Detail>
</xsl:template>
</xsl:stylesheet>
...is applied to the original XML:
<?xml version="1.0" encoding="UTF-8"?>
<File>
<Detail type="A" group="1">
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
<Detail type="A" group="1">
<Data>
<Nr>6</Nr>
</Data>
</Detail>
</File>
...the wanted result is produced:
<?xml version="1.0" encoding="utf-8"?>
<File>
<Detail type="A" group="1">
<Data>
<Nr>1</Nr>
</Data>
<Data>
<Nr>2</Nr>
</Data>
<Data>
<Nr>6</Nr>
</Data>
</Detail>
<Detail type="B" group="1">
<Data>
<Nr>3</Nr>
</Data>
<Data>
<Nr>4</Nr>
</Data>
</Detail>
<Detail type="B" group="2">
<Data>
<Nr>5</Nr>
</Data>
</Detail>
</File>
Explanation:
By properly applying the Muenchian Grouping Method (which is necessary in XSLT 1.0, given that it does not have any "inline" grouping mechanism), we can find unique nodes and group their descendants.
II. XSLT 2.0
When this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/*">
<File>
<xsl:for-each-group select="Detail"
group-by="concat(#type, '+', #group)">
<Detail type="{#type}" group="{#group}">
<xsl:copy-of select="current-group()/Data" />
</Detail>
</xsl:for-each-group>
</File>
</xsl:template>
</xsl:stylesheet>
...is applied to the original XML, the same correct result is produced.
Explanation:
By properly applying XSLT 2.0's for-each-group element, we can arrive at the same result.
NOTE: This question languished in the bin for 6 hours before I took it up. My answer languished in the bin for two hours before someone else disguised some non-essential comments as an answer.
Study up on Muenchian grouping. It's handy for these grouping problems.
The heavy lifters are <xsl:key>, which creates a key based on a concat of #type and #group, and this line here, <xsl:for-each select="File/Detail[count(. | key('details', concat(#type,'_',#group))[1]) = 1]">, which isolates the first occurrence of the Detail node having a particular key and by extension a particular pairing of #group and #type.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="details" match="Detail"
use="concat(#type,'_',#group)"/>
<xsl:template match='/'>
<File>
<xsl:for-each select="File/Detail[count(. | key('details', concat(#type,'_',#group))[1]) = 1]">
<xsl:sort select="concat(#type,'_',#group)" />
<Detail type="{#type}" group="{#group}">
<xsl:for-each select="key('details', concat(#type,'_',#group))">
<xsl:copy-of select="Data"/>
</xsl:for-each>
</Detail>
</xsl:for-each>
</File>
</xsl:template>
</xsl:stylesheet>

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" />

Ancestors' same-name siblings

<corpus>
<header id="1">
<file>
<info>
<title id="A" />
</info>
</file>
</header>
<TEI>
<header id="2">
<file>
<info>
<title id="B" />
</info>
</file>
</header>
<header id="3">
<file>
<info>
<record>
<title id="C" />
</record>
</info>
</file>
</header>
<header id="4">
<file>
</file>
</header>
</TEI>
</corpus>
$list is a set of <title> nodes.
The depth of <title> varies, but is always somewhere under a <header>. The depth of <header> varies, but its depth from root is always the same for all nodes in a given $list.
Given a $list, I need a for-each loop that loops through headers.
When the only node in $list is title A, I need to loop only through header 1.
When the nodes in $list are titles B and C, I need to loop through headers 2, 3 and 4.
Use:
$list/ancestor::header[1]/../header
XSLT - based verification:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vList1" select=
"/*/header[1]/*/*/title"/>
<xsl:variable name="vList2" select=
"/*/TEI//title"/>
<xsl:template match="/">
<xsl:copy-of select="$vList1"/>
====
<xsl:copy-of select="$vList2"/>
====
<xsl:copy-of select=
"$vList1/ancestor::header[1]/../header"/>
====
<xsl:copy-of select=
"$vList2/ancestor::header[1]/../header"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<corpus>
<header id="1">
<file>
<info>
<title id="A" />
</info>
</file>
</header>
<TEI>
<header id="2">
<file>
<info>
<title id="B" />
</info>
</file>
</header>
<header id="3">
<file>
<info>
<record>
<title id="C" />
</record>
</info>
</file>
</header>
<header id="4">
<file>
</file>
</header>
</TEI>
</corpus>
the XPath expressions are evaluated and the wanted in each case header elements are selected and output:
<title id="A"/>
====
<title id="B"/>
<title id="C"/>
====
<header id="1">
<file>
<info>
<title id="A"/>
</info>
</file>
</header>
====
<header id="2">
<file>
<info>
<title id="B"/>
</info>
</file>
</header>
<header id="3">
<file>
<info>
<record>
<title id="C"/>
</record>
</info>
</file>
</header>
<header id="4">
<file>
</file>
</header>