Reputation: 185
I need to remove some nodes in my xml file in which it contains certain method and by what order it appears for example here is the input:
<root>
<node id="a">
<section id="a_1">
<item id="0">
<attribute>
<color>Red</color>
</attribute>
</item>
</section>
<section id="a_2">
<item id="0">
<attribute>
<color>Red</color>
</attribute>
</item>
</section>
</node>
<node id="b">
<section id="b_1">
<user id="b_1b" method="pause">
<attribute>a</attribute>
</user>
<user id="b_1b" method="run">
<attribute>a</attribute>
</user>
<user id="b_1a" method="run">
<attribute>
<name>John</name>
</attribute>
</user>
<user id="b_1a" method="pause">
<attribute>a</attribute>
</user>
</section>
<section id="b_1" method="create">
<user id="b_1b" method="stop">
<attribute>a</attribute>
</user>
<user id="b_1a" method="stop">
<attribute>a</attribute>
</user>
<user id="b_1b" method="run">
<attribute>a</attribute>
</user>
<user id="b_1b" method="pause">
<attribute>a</attribute>
</user>
</section>
<section id="b_2">
<user id="b_1a" method="run">
<attribute>
<name>John</name>
</attribute>
</user>
</section>
</node>
If I use this method:
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://localhost"
exclude-result-prefixes="my">
<!-- Copy everything by default -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match=" user[not(@method eq 'stop' )][not(. is my:last(.))]
| user[ @method eq 'stop' ][not(. is my:last(.))]
| user[not(@method eq 'stop')][my:last(.)/@method eq 'stop']"/>
<!-- Get the last user for this section & user id -->
<xsl:function name="my:last">
<xsl:param name="user"/>
<xsl:sequence select="($user/../../section[@id eq $user/../@id]
/user [@id eq $user/@id]
)
[last()]"/>
</xsl:function>
</xsl:transform>
result will be:
<root>
<node id="a">
<section id="a_1">
<item id="0">
<attribute>
<color>
Red
</color>
</attribute>
</item>
</section>
<section id="a_2">
<item id="0">
<attribute>
<color>
Red
</color>
</attribute>
</item>
</section>
</node>
<node id="b">
<section id="b_1">
</section>
<section id="b_1" method="create">
<user id="b_1a" method="stop">
<attribute>
a
</attribute>
</user>
<user id="b_1b" method="pause">
<attribute>
a
</attribute>
</user>
</section>
<section id="b_2">
<user id="b_1a" method="run">
<attribute>
<name>
John
</name>
</attribute>
</user>
</section>
</node>
</root>
while the expected output is:
<root>
<node id="a">
<section id="a_1">
<item id="0">
<attribute>
<color>Red</color>
</attribute>
</item>
</section>
<section id="a_2">
<item id="0">
<attribute>
<color>Red</color>
</attribute>
</item>
</section>
</node>
<node id="b">
<section id="b_1">
</section>
<section id="b_1" method="create">
<user id="b_1a" method="stop">
<attribute>a</attribute>
</user>
**<user id="b_1b" method="run">
<attribute>a</attribute>
</user>**
<user id="b_1b" method="pause">
<attribute>a</attribute>
</user>
</section>
<section id="b_2">
<user id="b_1a" method="run">
<attribute>
<name>John</name>
</attribute>
</user>
</section>
</node>
</root>
so this is how the order works. If 'stop' occurs last then any other node with other methods with the same user id (such as pause and run) will be removed. But if it's not then the node with 'stop' itself and All node before that 'stop' will be removed.
This has to be occurred in the same section id and it will delete only the user node (leave the section id even if it is empty after deletion of user node). Hope the explanation it's not confusing.
Is that possible to be done by using XSLT only? or do I have to use xQuery to traverse each node? Can someone enlighten me? Thank you very much.
John
Upvotes: 0
Views: 546
Reputation: 4126
Here's a solution using XSLT 2.0. The first rule is the canonical identity transform. The second rule removes the nodes you wanted to be removed (if I understood you correctly). Each list of users (as I now understand it) is identified by the combination of section id and user id.
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://localhost"
exclude-result-prefixes="my">
<!-- Copy everything by default -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- But remove the "stop" method if it's not last for the user/section;
also, remove other methods where the last one is "stop" -->
<xsl:template match="user[ @method eq 'stop' ][not(. is my:last(.))]
| user[not(@method eq 'stop')][my:last(.)/@method eq 'stop']"/>
<!-- Get the last user for this section & user id -->
<xsl:function name="my:last">
<xsl:param name="user"/>
<xsl:sequence select="($user/../../section[@id eq $user/../@id]
/user [@id eq $user/@id]
)
[last()]"/>
</xsl:function>
</xsl:transform>
Let me know if you have any questions about how the above works.
Upvotes: 2