xslt 2.0 sort elements by number - sorting

I have the below input XML,
for some reason my xslt is not able to sort it.
please advice.
appreciate your insights.
this is the full XML
......................................................
I want to sort the G_SHL segment with its content based on D_628 field.
`<?xml version="1.0" encoding="UTF-8"?>
<LIST>
<S_ISA>
<D_I01>00</D_I01>
<D_I02/>
<D_I03>00</D_I03>
<D_I04/>
<D_I05>01</D_I05>
<D_I06></D_I06>
<D_I05_2>ZZ</D_I05_2>
<D_I07></D_I07>
<D_I08>160427</D_I08>
<D_I09>1106</D_I09>
<D_I10>U</D_I10>
<D_I11>00401</D_I11>
<D_I12>000000001</D_I12>
<D_I13>0</D_I13>
<D_I14/>
<D_I15>></D_I15>
<S_GS>
<D_479>SH</D_479>
<D_142></D_142>
<D_124></D_124>
<D_373>20160427</D_373>
<D_337>1106</D_337>
<D_28>1</D_28>
<D_455>X</D_455>
<D_480>004010</D_480>
<S_ST>
<D_143>856</D_143>
<D_329>0001</D_329>
<S_BSN>
<D_353>00</D_353>
<D_396>0081664420</D_396>
<D_373>20160426</D_373>
<D_337>1347</D_337>
<D_1005>0001</D_1005>
</S_BSN>
<G_SHL>
<S_HL>
<D_628>0000001</D_628>
<D_734>0000000</D_734>
<D_735>S</D_735>
<S_PRF>
<D_324>SITT10-1447195769627</D_324>
<D_328>SITT10-1447195769627</D_328>
</S_PRF>
<S_MEA>
<D_737>PD</D_737>
<D_738>G</D_738>
<D_739>0.081</D_739>
<C_C001>
<D_355>KG</D_355>
</C_C001>
</S_MEA>
<S_TD1>
<D_103>PKG</D_103>
<D_80>00002</D_80>
<D_187>G</D_187>
<D_81>00000.18</D_81>
<D_355>LB</D_355>
</S_TD1>
<S_TD5>
<D_133>O</D_133>
<D_66>2</D_66>
<D_368>CC</D_368>
</S_TD5>
<S_REF>
<D_128>WH</D_128>
<D_127>WH:</D_127>
</S_REF>
<S_REF>
<D_128>RN</D_128>
<D_127>RN:</D_127>
</S_REF>
<S_DTM>
<D_374>011</D_374>
<D_373>20160414</D_373>
</S_DTM>
<G_SN1>
<S_N1>
<D_98>ST</D_98>
<D_93>C3333</D_93>
<S_N3>
<D_166>TEST</D_166>
</S_N3>
<S_N4>
<D_19>TEST</D_19>
<D_116>12345</D_116>
<D_26>US</D_26>
</S_N4>
</S_N1>
</G_SN1>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000002</D_628>
<D_734>0000001</D_734>
<D_735>O</D_735>
<S_MAN>
<D_88>CP</D_88>
<D_87></D_87>
</S_MAN>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000003</D_628>
<D_734>0000002</D_734>
<D_735>P</D_735>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000005</D_628>
<D_734>0000002</D_734>
<D_735>P</D_735>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000004</D_628>
<D_734>0000003</D_734>
<D_735>I</D_735>
<S_LIN>
<D_350>000010</D_350>
<D_235>SK</D_235>
<D_234></D_234>
<D_235_2>BP</D_235_2>
<D_234_2></D_234_2>
</S_LIN>
<S_SN1>
<D_350></D_350>
<D_382>2.000</D_382>
<D_355>EA</D_355>
<D_668>AC</D_668>
</S_SN1>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000006</D_628>
<D_734>0000005</D_734>
<D_735>I</D_735>
<S_LIN>
<D_350>000020</D_350>
<D_235></D_235>
<D_234></D_234>
<D_235_2>BP</D_235_2>
<D_234_2></D_234_2>
</S_LIN>
<S_SN1>
<D_382>1.000</D_382>
<D_355>EA</D_355>
<D_668>AC</D_668>
</S_SN1>
</S_HL>
</G_SHL>
<S_CTT>
<D_354>1</D_354>
</S_CTT>
<S_SE>
<D_96>[]</D_96>
<D_329>0001</D_329>
</S_SE>
</S_ST>
<S_GE>
<D_97>0</D_97>
<D_28>1</D_28>
</S_GE>
</S_GS>
<S_IEA>
<D_I16>01</D_I16>
<D_I12>000000001</D_I12>
</S_IEA>
</S_ISA>
</LIST>`

Try
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/LIST/S_ISA/S_GS/S_ST">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="G_SHL">
<xsl:sort select="S_HL/D_628" data-type="number" order="descending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
I can't verify the path /LIST/S_ISA/S_GS/S_ST but assume you have those ancestor elements in your real XML.
If there can be sibling elements you want to keep in their original input position then one way in XSLT 2.0 is to first identify the adjacent elements you want to sort with for-each-group group-adjacent:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/LIST/S_ISA/S_GS/S_ST">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="*" group-adjacent="boolean(self::G_SHL)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()">
<xsl:sort select="S_HL/D_628" data-type="number" order="descending"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"></xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT Sort on child subelements

I am trying to output this entire XML but with the EVENT elements sorted by ID. Being new to XSLT I thought I would still give it a try but after many attempts and reading other examples and how to guides I still can't get what I thought was a simple thing to work.
<?xml version="1.0" encoding="UTF-8"?>
<PublishWCWORKORDEROUT xmlns="http://www.xcessteel.com/maxo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creationDateTime="2021-05-10T08:23:18+00:00" transLanguage="EN" baseLanguage="EN" messageID="3116171.1620634998889850919" maxoVersion="7 6 20190514-1348 V7611-365" event="1">
<WCWORKORDEROUTSet>
<WORKORDER action="Replace">
<ACTCATEGORY />
<X_3857>1.1838832494481975E7</X_3857>
<Y_3857>-2766476.1752903816</Y_3857>
<SPEC>
<ALNVALUE />
<REFID xsi:nil="true" />
<ASSETATTRID>ACCOUNT_NO</ASSETATTRID>
<CHANGEBY>ADMIN</CHANGEBY>
</SPEC>
<SPEC>
<ALNVALUE />
<REFID xsi:nil="true" />
</SPEC>
<SPEC>
<ALNVALUE />
<REFID xsi:nil="true" />
<ASSETATTRID>METER_LOCATION</ASSETATTRID>
</SPEC>
<EVENT>
<ID>CCC333</ID>
<WORKTYPE>UNPLANNED</WORKTYPE>
</EVENT>
<EVENT>
<ID>AAA111</ID>
<WORKTYPE>PLANNED</WORKTYPE>
</EVENT>
<EVENT>
<ID>BBB222</ID>
<WORKTYPE>SCHEDULED</WORKTYPE>
</EVENT>
<ASSIGNMENT>
<AMCREW />
<WPLABORID>209336</WPLABORID>
</ASSIGNMENT>
<WCWODETAILS>
<REFID xsi:nil="true" />
<CUSTOMERNAME />
<WCREGION>SWR</WCREGION>
<ID>96057400</ID>
</WCWODETAILS>
</WORKORDER>
</WCWORKORDEROUTSet>
</PublishWCWORKORDEROUT>
I have tried with this XSLT but clearly it is not correct.
<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"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/PublishWCWORKORDEROUT">
<xsl:copy>
<xsl:apply-templates select="EVENT">
<xsl:sort select="ID"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You have to declare the namespace xmlns="http://www.xcessteel.com/maxo" and then use that prefix in your match and the sorting could be done like this:
EDIT on 2021-05-24 on 10:19: attribute action="Replace" was missing
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:maxo="http://www.xcessteel.com/maxo"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="maxo:WORKORDER">
<xsl:copy>
<!-- Following line was missing -->
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="maxo:EVENT[1]/preceding-sibling::*"/>
<xsl:apply-templates select="maxo:EVENT">
<xsl:sort select="maxo:ID"/>
</xsl:apply-templates>
<xsl:apply-templates select="maxo:EVENT[ position()=last()]/following-sibling::*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And as an alternative, the following would work as well:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:maxo="http://www.xcessteel.com/maxo"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="maxo:EVENT[not(preceding-sibling:: maxo:EVENT)]">
<xsl:for-each select=".|following-sibling:: maxo:EVENT">
<xsl:sort select="maxo:ID"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:template match="maxo:EVENT[ preceding-sibling:: maxo:EVENT]"/>
</xsl:stylesheet>

XSLT 1.0 to modify config file replace function

I am trying to write an XSLT to modify a config file. I have tried to use the replace function but that is only supported in 2.0 and i have tried to use translate but "true" trnaslated to "false" gets truncated to 'fals'. I cant just replace the whole modules section since our customers are in a distributed environment and I don't know if they have added any thing else to the section.
What I am starting with:
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
</modules>
Desired output:
<modules runAllManagedModulesForAllRequests="false">
<remove name="FormsAuthentication" />
</modules>
This is what I thought would do the trick.
<xsl:template match="/configuration/system.webServer/modules">
<xsl:choose>
<xsl:when test="#name=runAllManagedModulesForAllRequests">
<xsl:copy>
<xsl:copy-of select="<modules runAllManagedModulesForAllRequests="false">"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="#*"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Would this work for you?
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#runAllManagedModulesForAllRequests[.='true']">
<xsl:attribute name="runAllManagedModulesForAllRequests">false</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Xpath Query Help. Selecting data from multiple paths as a single data set

I have an xml structure like the following :
<doc>
<line void="false">
<lineNumber>1</lineNumber>
<info1>ddddd</info1>
<info2>aaaaa</info2>
</line>
<line void="true">
<lineNumber>2</lineNumber>
<voidLineNumber>1</voidLineNumber>
<voidValue>2.00</voidValue>
</line>
</doc>
I need one single set of data. I would like to select all the lines where void = false as well as the voidLineNumber and voidValue data from the line where void = true and the voidLineNumber = lineNumber from the original line.
Is this possible? Any help would be appreciated. Thanks
As Michael Kay noted, XPath itself can only be used to select nodes, not to transform them. You can do what you want with XSLT:
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes" />
<xsl:template match="doc">
<xsl:apply-templates select="line" />
</xsl:template>
<xsl:template match="line">
<xsl:variable name="voidvalue"><xsl:value-of select="#void" /></xsl:variable>
<xsl:if test="$voidvalue='false'">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:if test="name(.)='lineNumber'">
<xsl:value-of select="."/>
</xsl:if>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Output Context Node (full path) in XSLT 1.0?

For debugging purposes it would be handy to output the full path of the context node from within a template, is there unabbreviated xpath or function to report this ?
Example Template:
<xsl:template match="#first">
<tr>
<td>
<xsl:value-of select="??WHAT TO PUT IN HERE??"/>
</td>
</tr>
</xsl:template>
Example (Abridged) input document:
<people>
<person>
<name first="alan">
...
The output from the template would be something like:
people / person / name / #first
Or something similar.
This transformation produces an XPath expression for the wanted node:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:variable name="vNode" select=
"/*/*[2]/*/#first"/>
<xsl:apply-templates select="$vNode" mode="path"/>
</xsl:template>
<xsl:template match="*" mode="path">
<xsl:value-of select="concat('/',name())"/>
<xsl:variable name="vnumPrecSiblings" select=
"count(preceding-sibling::*[name()=name(current())])"/>
<xsl:variable name="vnumFollSiblings" select=
"count(following-sibling::*[name()=name(current())])"/>
<xsl:if test="$vnumPrecSiblings or $vnumFollSiblings">
<xsl:value-of select=
"concat('[', $vnumPrecSiblings +1, ']')"/>
</xsl:if>
</xsl:template>
<xsl:template match="#*" mode="path">
<xsl:apply-templates select="ancestor::*" mode="path"/>
<xsl:value-of select="concat('/#', name())"/>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<people>
<person>
<name first="betty" last="jones"/>
</person>
<person>
<name first="alan" last="smith"/>
</person>
</people>
the wanted, correct result is produced:
/people/person[2]/name/#first
Here's a stylesheet (of dubious value) that prints the path to every element and attribute in a document:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:strip-space elements="*" />
<xsl:template match="*">
<xsl:param name="pathToHere" select="''" />
<xsl:variable name="precSiblings"
select="count(preceding-sibling::*[name()=name(current())])" />
<xsl:variable name="follSiblings"
select="count(following-sibling::*[name()=name(current())])" />
<xsl:variable name="fullPath"
select="concat($pathToHere, '/', name(),
substring(concat('[', $precSiblings + 1, ']'),
1 div ($follSiblings or $precSiblings)))" />
<xsl:value-of select="concat($fullPath, '
')" />
<xsl:apply-templates select="#*|*">
<xsl:with-param name="pathToHere" select="$fullPath" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="#*">
<xsl:param name="pathToHere" select="''" />
<xsl:value-of select="concat($pathToHere, '/#', name(), '
')" />
</xsl:template>
</xsl:stylesheet>
When applied to this input:
<people>
<person>
<name first="betty" last="jones" />
</person>
<person>
<name first="alan" last="smith" />
</person>
<singleElement />
</people>
Produces:
/people
/people/person[1]
/people/person[1]/name
/people/person[1]/name/#first
/people/person[1]/name/#last
/people/person[2]
/people/person[2]/name
/people/person[2]/name/#first
/people/person[2]/name/#last
/people/singleElement

XSLT: copy object xml multiple times while incrementing attribute and value

I have a xml as below that I'd like to copy n times while incrementing one of its element and one of its attribute.
XML input:
<?xml version="1.0"?>
<header xmlns="http://test.com" >
<Batch>
<test document="dump" >
<Person position=1>
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
</header>
and I'd like something like below with the number of increment to be a variable.
XML output:
<?xml version="1.0"?>
<header xmlns="http://test.com" >
<Batch>
<test document="dump" >
<Person position=1>
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
<Person position=2>
<properties>
<name>John</name>
<number>2</number>
</properties>
</Person>
...
<Person position=n>
<properties>
<name>John</name>
<number>n</number>
</properties>
</Person>
</test>
</Batch>
</header>
To solve this, I've started with the xslt below:
<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:param name="pTimes" select="2"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="Person">
<xsl:param name="pPosition" select="1"/>
<xsl:value-of select="$newline"/>
<Person position="{$pPosition}">
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="number">
<xsl:param name="pPosition" select="1"/>
<number><xsl:value-of select="$pPosition"/></number>
</xsl:template>
</xsl:stylesheet>
but the output includes the namespace in elements. The element and attribute #position are always set to 1. Also, the header surrounds each element.
Please refer to the output below with n=2
<Batch xmlns="http://test.com">
<test document="dump">
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
<Batch xmlns="http://test.com">
<test document="dump">
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
Any clue?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://test.com"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="2"/>
<xsl:template match="node()|#*">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:apply-templates select="node()|#*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:test">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="t:Person">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="position">
<xsl:value-of select="$pPosition"/>
</xsl:attribute>
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:number">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:value-of select="$pPosition"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<header xmlns="http://test.com" >
<Batch>
<test document="dump" >
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
</header>
produces the wanted results:
<header xmlns="http://test.com">
<Batch>
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
<Person position="2">
<properties>
<name>John</name>
<number>2</number>
</properties>
</Person>
</Batch>
</header>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://test.com">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="2"/>
<xsl:template match="node()|#*">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:apply-templates select="node()|#*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:test">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>`enter code here`
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="t:Person">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="position">
<xsl:value-of select="$pPosition"/>
</xsl:attribute>
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:number">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:value-of select="$pPosition"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Resources