By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,903 Members | 1,922 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,903 IT Pros & Developers. It's quick & easy.

remove nodes that belong to a namespace

P: n/a
Hi all,
Following is a portion of an XML document. I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following
example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?

<ns0:Proposal>
<ns0:Company>
<w:p>
<w:r>
<w:t>test company</w:t>
</w:r>
</w:p>
</ns0:Company>
</ns0:Proposal>

I tried following xpath just to select all nodes that belong to ns0
namespace. But it did not work.
XmlNodeList oNodeList = oXMLWordDoc.SelectNodes("//ns0:*",oNSMgr);

I also tried this with no avail.

XmlNodeList oNodeList = oXMLWordDoc.SelectNodes("ns0:*",oNSMgr);

Thanks.
Nov 12 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
"Nikhil Patel" <ni********@aol.com> wrote in message news:Oj*************@TK2MSFTNGP10.phx.gbl...
I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following
example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?


Simple matter of walking the node tree, throwing out nodes with
the namespace URI corresponding to the ns0 prefix, while cloning
and "bubbling" up their children.

Here's a little recursive routine that should accomplish that:

- - - ElementNsFilter.cs (partial)
// . . .
public void RemoveNS( XmlNode node, string nsURI)
{
// Descend first.
if ( node.HasChildNodes )
RemoveNS( node.FirstChild, nsURI);

// Then move laterally.
if ( node.NextSibling != null )
RemoveNS( node.NextSibling, nsURI);

// Then ignore text, PI, comment, CDATA nodes.
if ( node.NodeType == XmlNodeType.Element )

// Your filter criterion goes here.
if ( node.NamespaceURI == nsURI )
{

// Must copy the child nodes to bubble up.
XmlNodeList xnl = node.ChildNodes;
int i = -1, iMax = xnl.Count;
ArrayList copies = new ArrayList( xnl.Count);
while ( ++i < iMax )
copies.Add( xnl[ i].Clone());

// Insert child nodes in document order as they appear
// beneath this node, prior to it in it's parent's node coll.
i = -1;
while ( ++i < iMax )
node.ParentNode.InsertBefore( (XmlNode)( copies[ i]), node);

// Remove this node (the copied children now exist in
// the parent's node coll).
node.ParentNode.RemoveChild( node);
}
}
// . . .
- - -

The reason a copy of child nodes is necessary, is that the more straight-
forward loop:

foreach( XmlNode xn in node.ChildNodes)
node.ParentNode.InsertBefore( xn, node);

Actually incurs unintended side effects in the ChildNodes collection. The
insertion of a child node before this node in the parent collection "bumps"
the nodes ahead of 'node', and the XmlNode iterator will end up counting
'node' as one of it's own children.

Another point to make, is that while it isn't the situation with your example
XML instance document, removing all elements of a specific namespace
can leave you with an invalid XML document. Suppose the document were
as follows:

- - - ValidXml.xml
<?xml version="1.0" ?>
<ns0:Proposal xmlns:ns0="http://schemas.example.com/FIRST" xmlns:w="http://schemas.example.com/SECOND">
<w:p>
</w:p>
<w:r>
</w:r>
</ns0:Proposal>
- - -

Naive processing of this document will result in the following invalid XML,
because an XML document must have exactly one document element
(this instance has two).

- - - InvalidXml.xml
<?xml version="1.0" ?>
<w:p>
</w:p>
<w:r><!-- ERROR: Second document root element. -->
</w:r>
- - -

Thus, when calling RemoveNS( ) or something similar, it's highly recommended
you nest your document in an artificial root element (whose namespace URI is
different from that you are removing), like this.

// Move the existing XML document inside of an artificial root, in
// case the results of namespace element removal would produce
// multiple document elements.

XmlElement root = xmlDoc.CreateElement("root");
root.AppendChild( xmlDoc.DocumentElement);
xmlDoc.AppendChild( root);

RemoveNS( root, "http://schemas.example.com/FIRST");

Then you can properly test the resulting document after RemoveNS()
has been called by counting the number of child nodes (if there are
more than one element node beneath the artificial root, then you would
throw an exception that the result of the removal of all elements of the
requested namespace is an invalid XML document with multiple root
elements).

if ( xmlDoc.DocumentElement.ChildNodes.Count > 1 )
{
throw new XmlException( String.Format( "Result of removing all"+
" elements with namespace URI {0} is a document with multiple"+
" root elements.", null);
}

Otherwise, you can pull out the artificial root element and elevate the
resulting node tree (sans elements of the namespace URI you wanted
to filter out).
Derek Harmon
Nov 12 '05 #2

P: n/a
"Nikhil Patel" <ni********@aol.com> wrote in message news:Oj*************@TK2MSFTNGP10.phx.gbl...
I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following
example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?


Simple matter of walking the node tree, throwing out nodes with
the namespace URI corresponding to the ns0 prefix, while cloning
and "bubbling" up their children.

Here's a little recursive routine that should accomplish that:

- - - ElementNsFilter.cs (partial)
// . . .
public void RemoveNS( XmlNode node, string nsURI)
{
// Descend first.
if ( node.HasChildNodes )
RemoveNS( node.FirstChild, nsURI);

// Then move laterally.
if ( node.NextSibling != null )
RemoveNS( node.NextSibling, nsURI);

// Then ignore text, PI, comment, CDATA nodes.
if ( node.NodeType == XmlNodeType.Element )

// Your filter criterion goes here.
if ( node.NamespaceURI == nsURI )
{

// Must copy the child nodes to bubble up.
XmlNodeList xnl = node.ChildNodes;
int i = -1, iMax = xnl.Count;
ArrayList copies = new ArrayList( xnl.Count);
while ( ++i < iMax )
copies.Add( xnl[ i].Clone());

// Insert child nodes in document order as they appear
// beneath this node, prior to it in it's parent's node coll.
i = -1;
while ( ++i < iMax )
node.ParentNode.InsertBefore( (XmlNode)( copies[ i]), node);

// Remove this node (the copied children now exist in
// the parent's node coll).
node.ParentNode.RemoveChild( node);
}
}
// . . .
- - -

The reason a copy of child nodes is necessary, is that the more straight-
forward loop:

foreach( XmlNode xn in node.ChildNodes)
node.ParentNode.InsertBefore( xn, node);

Actually incurs unintended side effects in the ChildNodes collection. The
insertion of a child node before this node in the parent collection "bumps"
the nodes ahead of 'node', and the XmlNode iterator will end up counting
'node' as one of it's own children.

Another point to make, is that while it isn't the situation with your example
XML instance document, removing all elements of a specific namespace
can leave you with an invalid XML document. Suppose the document were
as follows:

- - - ValidXml.xml
<?xml version="1.0" ?>
<ns0:Proposal xmlns:ns0="http://schemas.example.com/FIRST" xmlns:w="http://schemas.example.com/SECOND">
<w:p>
</w:p>
<w:r>
</w:r>
</ns0:Proposal>
- - -

Naive processing of this document will result in the following invalid XML,
because an XML document must have exactly one document element
(this instance has two).

- - - InvalidXml.xml
<?xml version="1.0" ?>
<w:p>
</w:p>
<w:r><!-- ERROR: Second document root element. -->
</w:r>
- - -

Thus, when calling RemoveNS( ) or something similar, it's highly recommended
you nest your document in an artificial root element (whose namespace URI is
different from that you are removing), like this.

// Move the existing XML document inside of an artificial root, in
// case the results of namespace element removal would produce
// multiple document elements.

XmlElement root = xmlDoc.CreateElement("root");
root.AppendChild( xmlDoc.DocumentElement);
xmlDoc.AppendChild( root);

RemoveNS( root, "http://schemas.example.com/FIRST");

Then you can properly test the resulting document after RemoveNS()
has been called by counting the number of child nodes (if there are
more than one element node beneath the artificial root, then you would
throw an exception that the result of the removal of all elements of the
requested namespace is an invalid XML document with multiple root
elements).

if ( xmlDoc.DocumentElement.ChildNodes.Count > 1 )
{
throw new XmlException( String.Format( "Result of removing all"+
" elements with namespace URI {0} is a document with multiple"+
" root elements.", null);
}

Otherwise, you can pull out the artificial root element and elevate the
resulting node tree (sans elements of the namespace URI you wanted
to filter out).
Derek Harmon
Nov 12 '05 #3

P: n/a
Nikhil Patel wrote:
Following is a portion of an XML document. I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following
example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?

<ns0:Proposal>
<ns0:Company>
<w:p>
<w:r>
<w:t>test company</w:t>
</w:r>
</w:p>
</ns0:Company>
</ns0:Proposal>


There is zillon ways of doing it. Which one is better depends on
particular details of your design.
Here is XSLT solution (which is usually the most simple and maintainable
one):

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="foo">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:Proposal|ns0:Company">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

--
Oleg Tkachenko [XML MVP]
http://blog.tkachenko.com
Nov 12 '05 #4

P: n/a
Nikhil Patel wrote:
Following is a portion of an XML document. I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following
example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?

<ns0:Proposal>
<ns0:Company>
<w:p>
<w:r>
<w:t>test company</w:t>
</w:r>
</w:p>
</ns0:Company>
</ns0:Proposal>


There is zillon ways of doing it. Which one is better depends on
particular details of your design.
Here is XSLT solution (which is usually the most simple and maintainable
one):

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="foo">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:Proposal|ns0:Company">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

--
Oleg Tkachenko [XML MVP]
http://blog.tkachenko.com
Nov 12 '05 #5

P: n/a
Thanks.

"Derek Harmon" <lo*******@msn.com> wrote in message
news:ud**************@TK2MSFTNGP11.phx.gbl...
"Nikhil Patel" <ni********@aol.com> wrote in message news:Oj*************@TK2MSFTNGP10.phx.gbl...
I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?


Simple matter of walking the node tree, throwing out nodes with
the namespace URI corresponding to the ns0 prefix, while cloning
and "bubbling" up their children.

Here's a little recursive routine that should accomplish that:

- - - ElementNsFilter.cs (partial)
// . . .
public void RemoveNS( XmlNode node, string nsURI)
{
// Descend first.
if ( node.HasChildNodes )
RemoveNS( node.FirstChild, nsURI);

// Then move laterally.
if ( node.NextSibling != null )
RemoveNS( node.NextSibling, nsURI);

// Then ignore text, PI, comment, CDATA nodes.
if ( node.NodeType == XmlNodeType.Element )

// Your filter criterion goes here.
if ( node.NamespaceURI == nsURI )
{

// Must copy the child nodes to bubble up.
XmlNodeList xnl = node.ChildNodes;
int i = -1, iMax = xnl.Count;
ArrayList copies = new ArrayList( xnl.Count);
while ( ++i < iMax )
copies.Add( xnl[ i].Clone());

// Insert child nodes in document order as they appear
// beneath this node, prior to it in it's parent's node coll.
i = -1;
while ( ++i < iMax )
node.ParentNode.InsertBefore( (XmlNode)( copies[ i]),

node);
// Remove this node (the copied children now exist in
// the parent's node coll).
node.ParentNode.RemoveChild( node);
}
}
// . . .
- - -

The reason a copy of child nodes is necessary, is that the more straight-
forward loop:

foreach( XmlNode xn in node.ChildNodes)
node.ParentNode.InsertBefore( xn, node);

Actually incurs unintended side effects in the ChildNodes collection. The
insertion of a child node before this node in the parent collection "bumps" the nodes ahead of 'node', and the XmlNode iterator will end up counting
'node' as one of it's own children.

Another point to make, is that while it isn't the situation with your example XML instance document, removing all elements of a specific namespace
can leave you with an invalid XML document. Suppose the document were
as follows:

- - - ValidXml.xml
<?xml version="1.0" ?>
<ns0:Proposal xmlns:ns0="http://schemas.example.com/FIRST" xmlns:w="http://schemas.example.com/SECOND"> <w:p>
</w:p>
<w:r>
</w:r>
</ns0:Proposal>
- - -

Naive processing of this document will result in the following invalid XML, because an XML document must have exactly one document element
(this instance has two).

- - - InvalidXml.xml
<?xml version="1.0" ?>
<w:p>
</w:p>
<w:r><!-- ERROR: Second document root element. -->
</w:r>
- - -

Thus, when calling RemoveNS( ) or something similar, it's highly recommended you nest your document in an artificial root element (whose namespace URI is different from that you are removing), like this.

// Move the existing XML document inside of an artificial root, in
// case the results of namespace element removal would produce
// multiple document elements.

XmlElement root = xmlDoc.CreateElement("root");
root.AppendChild( xmlDoc.DocumentElement);
xmlDoc.AppendChild( root);

RemoveNS( root, "http://schemas.example.com/FIRST");

Then you can properly test the resulting document after RemoveNS()
has been called by counting the number of child nodes (if there are
more than one element node beneath the artificial root, then you would
throw an exception that the result of the removal of all elements of the
requested namespace is an invalid XML document with multiple root
elements).

if ( xmlDoc.DocumentElement.ChildNodes.Count > 1 )
{
throw new XmlException( String.Format( "Result of removing all"+
" elements with namespace URI {0} is a document with multiple"+ " root elements.", null);
}

Otherwise, you can pull out the artificial root element and elevate the
resulting node tree (sans elements of the namespace URI you wanted
to filter out).
Derek Harmon

Nov 12 '05 #6

P: n/a
Thanks.

"Derek Harmon" <lo*******@msn.com> wrote in message
news:ud**************@TK2MSFTNGP11.phx.gbl...
"Nikhil Patel" <ni********@aol.com> wrote in message news:Oj*************@TK2MSFTNGP10.phx.gbl...
I need to remove all nodes
that belong to ns0 without deleting their child nodes. So in the following example , I want to delete "ns0:Proposal" and "ns0:Company" but I do not
want to delete their child nodes("w:p","w:r","w:t"). How can I do this?


Simple matter of walking the node tree, throwing out nodes with
the namespace URI corresponding to the ns0 prefix, while cloning
and "bubbling" up their children.

Here's a little recursive routine that should accomplish that:

- - - ElementNsFilter.cs (partial)
// . . .
public void RemoveNS( XmlNode node, string nsURI)
{
// Descend first.
if ( node.HasChildNodes )
RemoveNS( node.FirstChild, nsURI);

// Then move laterally.
if ( node.NextSibling != null )
RemoveNS( node.NextSibling, nsURI);

// Then ignore text, PI, comment, CDATA nodes.
if ( node.NodeType == XmlNodeType.Element )

// Your filter criterion goes here.
if ( node.NamespaceURI == nsURI )
{

// Must copy the child nodes to bubble up.
XmlNodeList xnl = node.ChildNodes;
int i = -1, iMax = xnl.Count;
ArrayList copies = new ArrayList( xnl.Count);
while ( ++i < iMax )
copies.Add( xnl[ i].Clone());

// Insert child nodes in document order as they appear
// beneath this node, prior to it in it's parent's node coll.
i = -1;
while ( ++i < iMax )
node.ParentNode.InsertBefore( (XmlNode)( copies[ i]),

node);
// Remove this node (the copied children now exist in
// the parent's node coll).
node.ParentNode.RemoveChild( node);
}
}
// . . .
- - -

The reason a copy of child nodes is necessary, is that the more straight-
forward loop:

foreach( XmlNode xn in node.ChildNodes)
node.ParentNode.InsertBefore( xn, node);

Actually incurs unintended side effects in the ChildNodes collection. The
insertion of a child node before this node in the parent collection "bumps" the nodes ahead of 'node', and the XmlNode iterator will end up counting
'node' as one of it's own children.

Another point to make, is that while it isn't the situation with your example XML instance document, removing all elements of a specific namespace
can leave you with an invalid XML document. Suppose the document were
as follows:

- - - ValidXml.xml
<?xml version="1.0" ?>
<ns0:Proposal xmlns:ns0="http://schemas.example.com/FIRST" xmlns:w="http://schemas.example.com/SECOND"> <w:p>
</w:p>
<w:r>
</w:r>
</ns0:Proposal>
- - -

Naive processing of this document will result in the following invalid XML, because an XML document must have exactly one document element
(this instance has two).

- - - InvalidXml.xml
<?xml version="1.0" ?>
<w:p>
</w:p>
<w:r><!-- ERROR: Second document root element. -->
</w:r>
- - -

Thus, when calling RemoveNS( ) or something similar, it's highly recommended you nest your document in an artificial root element (whose namespace URI is different from that you are removing), like this.

// Move the existing XML document inside of an artificial root, in
// case the results of namespace element removal would produce
// multiple document elements.

XmlElement root = xmlDoc.CreateElement("root");
root.AppendChild( xmlDoc.DocumentElement);
xmlDoc.AppendChild( root);

RemoveNS( root, "http://schemas.example.com/FIRST");

Then you can properly test the resulting document after RemoveNS()
has been called by counting the number of child nodes (if there are
more than one element node beneath the artificial root, then you would
throw an exception that the result of the removal of all elements of the
requested namespace is an invalid XML document with multiple root
elements).

if ( xmlDoc.DocumentElement.ChildNodes.Count > 1 )
{
throw new XmlException( String.Format( "Result of removing all"+
" elements with namespace URI {0} is a document with multiple"+ " root elements.", null);
}

Otherwise, you can pull out the artificial root element and elevate the
resulting node tree (sans elements of the namespace URI you wanted
to filter out).
Derek Harmon

Nov 12 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.