"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