How to copy all child elements except specific one
Dec 27th, 2008 by paolo
If you need to copy an XML document to another filtering it from a specific element node, a good solution could be the use of XSL. The starting point is the so called "identity" template as introduced with an example in the XSLT Recommendation itself:
-
<xsl:template match="@*|node()">
-
<xsl:copy>
-
<xsl:apply-templates select="@*|node()"/>
-
</xsl:copy>
-
</xsl:template>
The above template copies all attributes, @*, and all nodes being children of other nodes, node(): element nodes, text nodes, comment nodes and processing instruction nodes. Everything is copied. "Recursion" in the above case is when the template ends its job by calling other templates including itself.
Copying everything is no fun but becomes very useful when we add exceptions to the copying. It gets useful when we "copy everything but". The step by step approach in the copying is the big trick. For each attribute, element, etc., we can decide to do something else by adding other templates overruling the copying behavior.
Let's start hacking our XSL stylesheet to do something more fun...
1. The Input
In the examples we will use the following file as xml input.
-
<?xml version="1.0" encoding="UTF-8"?>
-
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
-
"http://www.docbook.org/xml/4.4/docbookx.dtd">
-
<book>
-
<bookinfo>
-
<title>Beginning XML</title>
-
</bookinfo>
-
<part>
-
<title>First Part </title>
-
<chapter>
-
<title>What is XML?</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>Well-Formed XML.</title>
-
<para>bla bla</para>
-
</chapter>
-
</part>
-
<part>
-
<title>Second Part </title>
-
<chapter>
-
<title>XML Namespaces.</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>XSLT.</title>
-
<para>bla bla</para>
-
</chapter>
-
</part>
-
</book>
2. Filter a specific element
2.1. The Stylesheet
-
<xsl:template match="@*|node()">
-
<xsl:copy>
-
<xsl:apply-templates select="@*|node()"/>
-
</xsl:copy>
-
</xsl:template>
-
-
<xsl:template match="part">
-
<xsl:copy-of select="*[not(self::thisElement)]"/>
-
</xsl:template>
The first template matches all attributes and all nodes being children of other nodes, and copies them over.
The second template matches the part elements of the context element and makes a copy of them discarting the self::thisElement. The first template puts node after node in context making it possible for the second template to match the part elements.
2.2. The Output
-
<?xml version="1.0" encoding="UTF-8"?>
-
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
-
"http://www.docbook.org/xml/4.4/docbookx.dtd">
-
<book>
-
<bookinfo>
-
<title>Beginning XML</title>
-
</bookinfo>
-
<title>First Part </title>
-
<chapter>
-
<title>What is XML?</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>Well-Formed XML.</title>
-
<para>bla bla</para>
-
</chapter>
-
<title>Second Part </title>
-
<chapter>
-
<title>XML Namespaces.</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>XSLT.</title>
-
<para>bla bla</para>
-
</chapter>
-
</book>
The output is not valid because part's title has been copied as all the other childs of part elements.
3. Filter a specific element and one of its child
3.1. The Stylesheet
-
<xsl:template match="@*|node()">
-
<xsl:copy>
-
<xsl:apply-templates select="@*|node()"/>
-
</xsl:copy>
-
</xsl:template>
-
-
<xsl:template match="part">
-
<xsl:for-each select="*">
-
<xsl:if test="name()!='title'">
-
<xsl:copy>
-
<xsl:apply-templates select="@*|node()"/>
-
</xsl:copy>
-
</xsl:if>
-
</xsl:for-each>
-
</xsl:template>
As before, the first template matches all attributes and all nodes being children of other nodes, and copies them over.
The second template matches the part elements of the context element and makes a copy of their childs skipping those of them called title.
3.2. The Output
-
<?xml version="1.0" encoding="UTF-8"?>
-
<!DOCTYPE book
-
PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd">
-
<book>
-
<bookinfo>
-
<title>Beginning XML</title>
-
</bookinfo>
-
<chapter>
-
<title>What is XML?</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>Well-Formed XML.</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>XML Namespaces.</title>
-
<para>bla bla</para>
-
</chapter>
-
<chapter>
-
<title>XSLT.</title>
-
<para>bla bla</para>
-
</chapter>
-
</book>
That's all: part elements has been filtered!

