Reputation: 2243
My Input XML File looks like
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
<segment id="ORC"/>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
in My above input XML file element segment with id="ORC" is optional
I want to group My input XML file based on element segment with id="ORC" or element segment with id="OBR"
for above Input XML file I want to have below result when element segment with id="ORC" is present
<message-group>
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
</test-message>
<test-message>
<segment id="ORC"/>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
</message-group>
for above Input XML file I want to have below result when element segment with id="ORC" is not present
<message-group>
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
</test-message>
<test-message>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
</message-group>
Can I have the XSLT (2.0) template or function to handle the above scenario
Note : I am making use of XSLT 2.0 and saxon parsers
Upvotes: 1
Views: 604
Reputation: 163468
If you're always splitting the sequence into exactly two groups, then I think I would do this:
<xsl:variable name="split" select="segment[@id=('ORC', 'OBR')][1]"/>
<test-message>
<xsl:copy-of select="$split/preceding-sibling::*"/>
</test-message>
<test-message>
<xsl:copy-of select="$split, $split/following-sibling::*"/>
</test-message>
Upvotes: 0
Reputation: 243529
This transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<message-group>
<xsl:for-each-group select="*" group-starting-with=
"segment[@id='ORC'][not(preceding-sibling::segment[1][@id='OBR'])]
| segment[@id='OBR'][not(preceding-sibling::segment[1][@id='ORC'])]
">
<test-message><xsl:sequence select="current-group()"/></test-message>
</xsl:for-each-group>
</message-group>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
<segment id="ORC"/>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
produces the wanted, correct result:
<message-group>
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
</test-message>
<test-message>
<segment id="ORC"/>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
</message-group>
When the same transformation (above) is applied on this XML document ('ORC' is not present):
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
again the wanted, correct result is produced:
<message-group>
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
</test-message>
<test-message>
<segment id="OBR"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
</message-group>
Wwhen the same transformation is applied on this XML document ('OBR' is not present):
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
<segment id="ORC"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
again the wanted, correct result is produced:
<message-group>
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
</test-message>
<test-message>
<segment id="ORC"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
</message-group>
Finally, when both 'ORC' and 'OBR' are present, but 'OBR' precedes 'ORC':
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
<segment id="OBR"/>
<segment id="ORC"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
Again the correct, wanted result is produced:
<message-group>
<test-message>
<segment id="MSH"/>
<segment id="SFT"/>
<segment id="PID"/>
<segment id="NTE"/>
<segment id="NK1"/>
<segment id="PV1"/>
</test-message>
<test-message>
<segment id="OBR"/>
<segment id="ORC"/>
<segment id="NTE"/>
<segment id="OBX"/>
<segment id="NTE"/>
<segment id="SPM"/>
</test-message>
</message-group>
Upvotes: 1
Reputation: 25034
There are two fairly simple ways that come quickly to mind.
(1) In your template for test-message
, include two test-message
output elements, each containing an apply-templates
instruction. Give apply-templates
a parameter to distinguish the first and second calls.
Say, something like (not tested):
<xsl:template match="test-message">
<test-message>
<xsl:apply-templates>
<xsl:with-param name="flag" select="1"/>
</xsl:apply-templates>
</test-message>
<test-message>
<xsl:apply-templates>
<xsl:with-param name="flag" select="2"/>
</xsl:apply-templates>
</test-message>
</xsl:template>
In the template for segment
, write out a copy of the element if (a) $flag = 1 and neither this segment nor any preceding sibling segment has an id of OBR
or ORC
, or else if (b) $flag = 2 and this segment or some preceding sibling segment has such an id
. Something like
<xsl:template match="segment">
<xsl:param name="flag"/>
<xsl:if test="(
$flag = 1
and not(@id = ('ORC', 'OBR'))
and not(preceding-sibling::segment
[@id=('ORC','OBR')])
) or (
$flag = 2
and ((@id = ('ORC', 'OBR'))
or preceding-sibling::segment
[@id=('ORC','OBR')]
)">
<xsl:copy-of select="."/>
</xsl:if>
(2) Make the test-message
template as above, but add select="./segment[1]"
to the two calls to xsl:apply-templates
.
Then make the template for segment
do its work and then recur on its immediately following sibling. To keep the logic simple, we distinguish several cases: first, $flag=1
and we haven't seen OBR or ORC yet: copy the current element and keep going.
<xsl:template match="segment">
<xsl:param name="flag"/>
<xsl:choose>
<xsl:when test="$flag=1 and not(@id=('OBR', 'ORC'))">
<xsl:copy-of select="."/>
<xsl:if test="not(@id=('OBR','ORC'))">
<xsl:apply-templates select="following-sibling::*[1]">
<xsl:with-param name="flag" select="$flag"/>
</xsl:apply-templates>
</xsl:if>
</xsl:when>
Second, $flag = 1
and we are now encountering OBR or ORC. Don't copy the current element and don't keep going; the first test-message
element is now completed.
<xsl:when test="$flag=1 and @id=('OBR', 'ORC')">
<!--* do nothing, stop recursion *-->
</xsl:when>
Third, $flag = 2
and we haven't yet encountered OBR or ORC. Keep going.
<xsl:when test="$flag=2 and not(@id=('OBR', 'ORC'))">
<!--* don't copy yet, keep looking for OBR/ORC *-->
<xsl:apply-templates select="following-sibling::*[1]">
<xsl:with-param name="flag" select="$flag"/>
</xsl:apply-templates>
</xsl:when>
Fourth, $flag = 2
and we are now encountering OBR or ORC. Copy the current element and keep going; switch the flag to a third value which means we are in the second test-message
element and we have seen OBR or ORC:
<xsl:when test="$flag=2 and @id=('OBR', 'ORC')">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::*[1]">
<xsl:with-param name="flag" select="3"/>
</xsl:apply-templates>
</xsl:when>
Finally, if $flag = 3
then we just copy the current element and keep going.
<xsl:when test="$flag=3">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::*[1]">
<xsl:with-param name="flag" select="3"/>
</xsl:apply-templates>
</xsl:when>
</xsl:choose>
</xsl:template>
Upvotes: 0