I have been having trouble transforming below xml using XSLT 2.0. Could anyone help me resolve this please? Thanks
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Row>
<EmployeeID>88888</EmployeeID>
<Remote/>
<StartDate>2014-10-19</StartDate>
<EndDate>2021-10-08</EndDate>
<Amount>100</Amount>
<BasePay>760</BasePay>
<Bonus>340</Bonus>
</Row>
<Row>
<EmployeeID>12345</EmployeeID>
<Remote/>
<StartDate>2017-10-10</StartDate>
<EndDate>2018-10-10</EndDate>
<Amount>250</Amount>
<BasePay>1500</BasePay>
<Bonus>150</Bonus>
</Row>
<Row>
<EmployeeID>23456</EmployeeID>
<Remote>N</Remote>
<StartDate/>
<EndDate></EndDate>
<Amount></Amount>
<BasePay>1500</BasePay>
<Bonus>150</Bonus>
</Row>
<Row>
<EmployeeID>23456</EmployeeID>
<Remote>N</Remote>
<StartDate/>
<EndDate>2020-10-10</EndDate>
<Amount>2400</Amount>
<BasePay>1500</BasePay>
<Bonus/>
</Row>
<Row>
<EmployeeID>23456</EmployeeID>
<Remote>N</Remote>
<StartDate>2018-10-24</StartDate>
<EndDate>2020-10-10</EndDate>
<Amount>2400</Amount>
<BasePay>1500</BasePay>
<Bonus/>
</Row>
<Row>
<EmployeeID>12345</EmployeeID>
<Remote/>
<StartDate>2017-10-10</StartDate>
<EndDate>2018-10-10</EndDate>
<Amount>350</Amount>
<BasePay>1600</BasePay>
<Bonus>170</Bonus>
</Row>
<Row>
<EmployeeID>65432</EmployeeID>
<Remote>Y</Remote>
<StartDate/>
<EndDate></EndDate>
<Amount></Amount>
<BasePay>1500</BasePay>
<Bonus>150</Bonus>
</Row>
<Row>
<EmployeeID>65432</EmployeeID>
<Remote>Y</Remote>
<StartDate>2021-10-25</StartDate>
<EndDate>2020-10-10</EndDate>
<Amount>8400</Amount>
<BasePay>1500</BasePay>
<Bonus/>
</Row>
</Root>
Desired Output
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Row>
<EmployeeID>88888</EmployeeID>
<Remote/>
<StartDate>2014-10-19</StartDate>
<EndDate>2021-10-08</EndDate>
<Amount>100</Amount>
<BasePay>760</BasePay>
<Bonus>340</Bonus>
</Row>
<Row>
<EmployeeID>12345</EmployeeID>
<Remote/>
<StartDate>2017-10-10</StartDate>
<EndDate>2018-10-10</EndDate>
<Amount>250</Amount>
<BasePay>1500</BasePay>
<Bonus>150</Bonus>
</Row>
<Row>
<EmployeeID>23456</EmployeeID>
<Remote>N</Remote>
<StartDate>2018-10-24</StartDate>
<EndDate>2020-10-10</EndDate>
<Amount>2400</Amount>
<BasePay>1500</BasePay>
<Bonus>150</Bonus>
</Row>
<Row>
<EmployeeID>12345</EmployeeID>
<Remote/>
<StartDate>2017-10-10</StartDate>
<EndDate>2018-10-10</EndDate>
<Amount>350</Amount>
<BasePay>1600</BasePay>
<Bonus>170</Bonus>
</Row>
<Row>
<EmployeeID>65432</EmployeeID>
<Remote>Y</Remote>
<StartDate>2021-10-25</StartDate>
<EndDate>2020-10-10</EndDate>
<Amount>8400</Amount>
<BasePay>1500</BasePay>
<Bonus>150</Bonus>
</Row>
</Root>
Grouping needs to be based on <Remote> element with value either Y or N.
For e.g. Employee 23456 appears thrice in the input xml with <Remote>set to N – Result xml is expected to have Employee 23456 only once but expected to pick up values for StartDate(i.e 2018-10-24) EndDate (i.e 2020-10-10) Amount(i.e. 2400)
For e.g. Employee 65432 appears twice in the input xml with value <Remote> set to Y – Result xml is expected to have Employee 65432 only once but expected to pick up values for StartDate (i.e 2021-10-25) EndDate (i.e 2020-10-10) Amount (8400)
Employee 12345 appears twice in the input xml with blank value for <Remote> - Result xml is expected to have Employee 12345 twice because <BasePay> and <Bonus> amounts are different.
Below is my attempt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"></xsl:output>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Root">
<xsl:copy>
<xsl:for-each-group select="Row" group-by="EmployeeID/[Mobile='Y' or 'N']">
<Row>
<EmployeeID>
<xsl:value-of select="EmployeeID"/>
</EmployeeID>
<Remote>
<xsl:value-of select="Remote"/>
</Remote>
<StartDate>
<xsl:value-of select="StartDate"/>
</StartDate>
<EndDate>
<xsl:value-of select="EndDate"/>
</EndDate>
<Amount>
<xsl:value-of select="Amount"/>
</Amount>
<BasePay>
<xsl:value-of select="BasePay"/>
</BasePay>
<Bonus>
<xsl:value-of select="Bonus"/>
</Bonus>
</Row>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Perhaps (using a composite grouping key as possible in XSLT 3)
<xsl:template match="/Root">
<xsl:copy>
<xsl:for-each-group select="Row" composite="yes" group-by="EmployeeID, Remote = ('Y', 'N')">
<xsl:choose>
<xsl:when test="current-grouping-key()[2]">
<xsl:apply-templates select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
Or using a composite key and then a match pattern that removes/strips the duplicates is easier to preserve the original order:
<xsl:key name="group" match="Root/Row" composite="yes" use="EmployeeID, Remote = ('Y', 'N')"/>
<xsl:template match="Root/Row[. intersect key('group', (EmployeeID, true()))[position() > 1]]"/>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
As for your comment about empty children, perhaps
<xsl:template match="Root/Row[. intersect key('group', (EmployeeID, true()))[1]]">
<xsl:copy>
<xsl:for-each-group select="key('group', (EmployeeID, true()))/*[node()]" group-by="node-name()">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
ensures the non-empty child elements in a group of Rows are output instead of the child elements of the first item in a group.
An online sample is here.
Related
INPUT XML:
<overline-start id="tie1" specific-use="tie-bar"/>PtCl<sub>2</sub>(P((CH<sub>2</sub>)<sub><italic toggle="yes">n</italic></sub>)<sub>3</sub><overline-end rid="tie1"/>
EXPECTED XML:
<overline id="tie1" specific-use="tie-bar">PtCl<sub>2</sub>(P((CH<sub>2</sub>)<sub><italic toggle="yes">n</italic></sub>)<sub>3</sub></overline>
MY XSLT 2.0 Code:
<xsl:template match="overline-start">
<xsl:for-each-group select="self::overline-start" group-adjacent="self::overline-start[following-sibling::overline-end]">
<xsl:for-each select="current-group()">
<overline>
<xsl:apply-templates select="#*"/>
<xsl:copy-of select="current-group()"/>
</overline>
</xsl:for-each>
As you mentioned the requirement in comments, I tried it #Martin Honnen 's way:
Assuming input as:
<?xml version="1.0" encoding="UTF-8"?>
<p>
<overline-start id="tie1" specific-use="tie-bar"/>PtCl<sub>2</sub>(P((CH<sub>2</sub>)<sub><italic toggle="yes">n</italic></sub>)<sub>3</sub><overline-end rid="tie1"/>
</p>
A 2.0 solution can be:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="no" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<overline>
<xsl:for-each-group select="* | text()" group-starting-with="overline-start">
<xsl:for-each-group select="current-group()" group-ending-with="overline-end">
<xsl:apply-templates select="#*" />
<xsl:sequence select="(current-group() except .) [position() != last()]" />
</xsl:for-each-group>
</xsl:for-each-group>
</overline>
</xsl:template>
</xsl:stylesheet>
http://xsltfiddle.liberty-development.net/bnnZW8/1
I have an issue in key function, key function not running in following code.
my input is
XML (Keys.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Keys>
<Key year="2001" name="ABC"/>
<Key year="2002" name="BCA"/>
</Keys>
XML to convert
<?xml version="1.0" encoding="UTF-8"?>
<p>
<text> .. .. <key>ABC</key> ...</text>
<text> .. .. <key>BCA</key> ...</text>
</p>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:key name="keydata" match="*[name() = document('keys.xml')]/Keys/Key" use="#name"/>
<xsl:template match="key">
<xsl:copy>
<xsl:attribute name="ref"><xsl:value-of select="key('keydata', .)/#year"/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output
<p>
<text> .. .. <key ref="">ABC</key> ...</text>
<text> .. .. <key ref="">BCA</key> ...</text>
</p>
Desired Ouput
<p>
<text> .. .. <key ref="2001">ABC</key> ...</text>
<text> .. .. <key ref="2002">BCA</key> ...</text>
</p>
This should work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:variable name="keys" select="document('keys.xml')" as="document-node()"/>
<xsl:key name="keydata" match="Key" use="#name"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="key">
<xsl:copy>
<xsl:attribute name="ref"><xsl:value-of select="$keys/key('keydata', current())/#year"/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In the XSL specification, there's an example entitled "Example: Using Keys to Reference other Documents" that exactly matches your use case.
This is the resulting document:
<?xml version="1.0" encoding="UTF-8"?>
<p>
<text> .. .. <key ref="2001">ABC</key> ...</text>
<text> .. .. <key ref="2002">BCA</key> ...</text>
</p>
Change <xsl:key name="keydata" match="*[name() = document('keys.xml')]/Keys/Key" use="#name"/> to <xsl:key name="keydata" match="Keys/Key" use="#name"/> and then <xsl:attribute name="ref"><xsl:value-of select="key('keydata', .)/#year"/></xsl:attribute> to <xsl:attribute name="ref" select="key('keydata', . doc('Keys.xml'))/#year"/></xsl:attribute>.
This is input xml input.xml
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
This is Prop.xml
<?xml version="1.0" encoding="utf-8"?>
<properties>
<code dpsi="0BZG" docid="asdww">HKBL1.0001.lohk.CAP65</code>
<code dpsi="0BZH" docid="navin">HKBL1.0002.aohk.CAP383</code>
<code no="3">345</code>
</properties>
This is desired output
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
IF prop.xml code/text matches remotelink/#refptid than copy attribute of prop.xml to remotelink otherwise no changes in remotelink.
This is the XSLT I have written. So far I am not getting result for unmatched condition:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink[#service='DOC-ID']" name="t-remote">
<xsl:variable name="refptid" select="./#refpt"/>
<xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>
<xsl:for-each select="$path/properties/code">
<xsl:choose>
<xsl:when test="./text()=$refptid">
<xsl:element name="remotelink">
<xsl:attribute name="DOC-ID" select="./#docid"/>
<xsl:attribute name="dpsi" select="./#dpsi"/>
<xsl:attribute name="refpt" select="$refptid"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:if test="./#docrefid"/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="remotelink[#service='DOC-ID']"> will never never be triggered, there's no service attribute on the <remotelink> element. Moreover, you don't retrieve the correct attribute (refpt instead of refptid)
This XSL transformation should do the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:variable name="props.doc" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')/properties" />
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink" name="t-remote">
<xsl:variable name="refptid" select="./#refptid"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:if test="$props.doc/code[. = $refptid]">
<xsl:apply-templates select="$props.doc/code[. = $refptid]/#*"/>
</xsl:if>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is the result of the transformation:
<?xml version="1.0" encoding="UTF-8"?><root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
I would simply define a global parameter or variable <xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>, then set up a key <xsl:key name="prop" match="code" use="."/>, and then use that in a template
<xsl:template match="remotelink[key('prop', #refptid, $path)]">
<xsl:copy>
<xsl:copy-of select="key('prop', #refptid, $path)/#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
that, together with your first template, should suffice.
below one is the XML input,
<?xml version="1.0" encoding="utf-8"?>
<GSK_Canonical_MESGX2>
<header SEGMENT="1">
<orderNumber>002001454979</orderNumber>
<batchNumber>0000617944</batchNumber>
<BOM SEGMENT="1">
<operationNumber>0030</operationNumber>
<phaseIndicator>0011</phaseIndicator>
</BOM>
<BOM SEGMENT="1">
<operationNumber>0040</operationNumber>
<phaseIndicator>0012</phaseIndicator>
</BOM>
<recipe SEGMENT="1">
<phase>0011</phase>
<parentOperation>0030</parentOperation>
<workcenter>MANUOHD1</workcenter>
</recipe>
<recipe SEGMENT="1">
<phase>0012</phase>
<parentOperation>0040</parentOperation>
<workcenter>COSTOHD1</workcenter>
</recipe>
</header>
</GSK_Canonical_MESGX2>
I have an below xslt,
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:set="http://exslt.org/sets" xmlns:str="http://exslt.org/strings" xmlns:java="http://xml.apache.org/xslt/java" xmlns:saxon="http://saxon.sf.net/" exclude-result-prefixes="exsl set str java saxon">
<xsl:output method="text"/>
<xsl:variable name="VarHash" select="'#'"/>
<xsl:variable name="VarBreak" select="'
'"/>
<xsl:variable name="pipeFieldDelimiter" select="'\|'"/>
<xsl:template match="/">
<xsl:text>HEADER</xsl:text>
<xsl:value-of select="$VarHash"/>
<xsl:value-of select="GSK_Canonical_MESGX2/header/orderNumber"/>
<xsl:value-of select="$VarHash"/>
<xsl:value-of select="GSK_Canonical_MESGX2/header/batchNumber"/>
<xsl:value-of select="$VarBreak"/>
<xsl:for-each select="GSK_Canonical_MESGX2/header/BOM">
<!--GSK_Canonical_MESGX2/Header/BOM/OperationNumber = GSK_Canonical_MESGX2/header/recipe/parentOperation and GSK_Canonical_MESGX2/Header/BOM/phaseIndicator = GSK_Canonical_MESGX2/header/recipe/phase then <xsl:value-of select="GSK_Canonical_MESGX2/header/recipe/workcenter"/> This needs to be implemented for each line item of BOM tag -->
<xsl:if test="position() != last()">
<xsl:value-of select="$VarBreak"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
below one is the expected output,
HEADER#002001454979#0000617944
MANUOHD1
COSTOHD1
now need to implement for each BOM line item,we need to compare BOM with Recipe tags and select workcenter value if the condition satisfied.
Header/BOM/OperationNumber = header/recipe/parentOperation
and
Header/BOM/phaseIndicator = header/recipe/phase
then
<xsl:value-of select="GSK_Canonical_MESGX2/header/recipe/workcenter"/>
Please help me to achieve this.Thanks
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kBomByPhaseAndOperation" match="BOM"
use="concat(operationNumber, '|', phaseIndicator)"/>
<xsl:template match=
"recipe[key('kBomByPhaseAndOperation',
concat(parentOperation, '|', phase))
]">
<xsl:value-of select="concat('
', workcenter)"/>
</xsl:template>
<xsl:template match="header">
<xsl:text>HEADER</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="orderNumber|batchNumber">
<xsl:value-of select="concat('#', .)"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When applied on the provided source XML document:
<GSK_Canonical_MESGX2>
<header SEGMENT="1">
<orderNumber>002001454979</orderNumber>
<batchNumber>0000617944</batchNumber>
<BOM SEGMENT="1">
<operationNumber>0030</operationNumber>
<phaseIndicator>0011</phaseIndicator>
</BOM>
<BOM SEGMENT="1">
<operationNumber>0040</operationNumber>
<phaseIndicator>0012</phaseIndicator>
</BOM>
<recipe SEGMENT="1">
<phase>0011</phase>
<parentOperation>0030</parentOperation>
<workcenter>MANUOHD1</workcenter>
</recipe>
<recipe SEGMENT="1">
<phase>0012</phase>
<parentOperation>0040</parentOperation>
<workcenter>COSTOHD1</workcenter>
</recipe>
</header>
</GSK_Canonical_MESGX2>
produces exactly the wanted, correct result:
HEADER#002001454979#0000617944
MANUOHD1
COSTOHD1
Move elements from one place to another using XSLT 2 (saxon8)
Hi all please help me to produce the following output using XSLT 2.0.
I have this:
<UL>
<ITEM>
aaa
<NL>iii
<ITEM1>111</ITEM1>
<ITEM2>222</ITEM2>
</NL>
</ITEM>
<ITEM>
bbb
<NL>vvv
<ITEM1>333</ITEM1>
<ITEM2>444</ITEM2>
</NL>
</ITEM>
</UL>
I need to produce this
<UL>
<ITEM>
aaa
<ITEM1>111</ITEM1>
</ITEM>
<ITEM>
bbb
<ITEM1>333</ITEM1>
</ITEM>
<NEW>
<NL>iii
<ITEM2>222</ITEM2>
</NL>
<NL>vvv
<ITEM2>444</ITEM2>
</NL>
</NEW>
</UL>
I am trying to get the output using mode option as mentioned below:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="NL"/>
<xsl:template match="ITEM2"/>
<xsl:template match="UL">
<xsl:copy>
<xsl:apply-templates/>
<NEW>
<xsl:apply-templates select="descendant::NL" mode="move"/>
</NEW>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM" mode="move">
<xsl:copy>
<xsl:apply-templates/>
<xsl:apply-templates select="descendant::ITEM2" mode="move1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="NL" mode="move">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM2" mode="move1">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Thanks and regards
Bala
Here is an XSLT 2.0 stylesheet that creates the structure you want, although white space is different, as it is a problem with mixed content to produce the exact output:
<?xml version="1.0" encoding="UTF-8"?>
<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:template match="UL">
<xsl:copy>
<xsl:for-each-group select="ITEM" group-by="NL/*/node-name(.)">
<xsl:choose>
<xsl:when test="position() eq 1">
<xsl:for-each select="current-group()">
<ITEM>
<xsl:apply-templates select="text() | NL/*[node-name(.) eq current-grouping-key()]"/>
</ITEM>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<NEW>
<xsl:for-each select="current-group()">
<NL>
<xsl:apply-templates select="NL/(text() | *[node-name(.) eq current-grouping-key()])"/>
</NL>
</xsl:for-each>
</NEW>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="NL/*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I get the result
<UL><ITEM>
aaa
<ITEM1>111</ITEM1>
</ITEM><ITEM>
bbb
<ITEM1>333</ITEM1>
</ITEM><NEW><NL>iii
<ITEM2>222</ITEM2>
</NL><NL>vvv
<ITEM2>444</ITEM2>
</NL></NEW></UL>
When I add instructions to strip white space from the input and indent the output, as in
<?xml version="1.0" encoding="UTF-8"?>
<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:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="UL">
<xsl:copy>
<xsl:for-each-group select="ITEM" group-by="NL/*/node-name(.)">
<xsl:choose>
<xsl:when test="position() eq 1">
<xsl:for-each select="current-group()">
<ITEM>
<xsl:apply-templates select="text() | NL/*[node-name(.) eq current-grouping-key()]"/>
</ITEM>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<NEW>
<xsl:for-each select="current-group()">
<NL>
<xsl:apply-templates select="NL/(text() | *[node-name(.) eq current-grouping-key()])"/>
</NL>
</xsl:for-each>
</NEW>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="NL/*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
the result is
<UL>
<ITEM>
aaa
<ITEM1>111</ITEM1>
</ITEM>
<ITEM>
bbb
<ITEM1>333</ITEM1>
</ITEM>
<NEW>
<NL>iii
<ITEM2>222</ITEM2>
</NL>
<NL>vvv
<ITEM2>444</ITEM2>
</NL>
</NEW>
</UL>
As an alternative, if we know there are just ITEM1 and ITEM2 we want to split, here is stylesheet using modes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="NL">
<xsl:apply-templates select="ITEM1"/>
</xsl:template>
<xsl:template match="ITEM2"/>
<xsl:template match="UL">
<xsl:copy>
<xsl:apply-templates/>
<NEW>
<xsl:apply-templates select="descendant::NL" mode="move"/>
</NEW>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM1" mode="move"/>
</xsl:stylesheet>