Convert contents of an XmlNodeList to a new XmlDocument without looping

1.7k views Asked by At

I have Xml that I filter using XPath (a query similar to this):

    XmlNodeList allItems = 
xDoc.SelectNodes("//Person[not(PersonID = following::Person/PersonID)]");

This filters all duplicates from my original Persons Xml. I want to create a new XmlDocument instance from the XmlNodeList generated above. At the minute, the only way I can see to do it is looping through the list of XmlNode's and building an Xml string (as such):

        XmlNodeList allItems = xDoc.SelectNodes("//Person[not(PersonID = following::Person/PersonID)]");
        StringBuilder xml = new StringBuilder("<Persons>");

        foreach (XmlNode node in allItems)
            xml.Append(node.OuterXml);

        xml.Append("</Persons>");

        XmlDocument newXDoc = new XmlDocument();
        newXDoc.LoadXml(xml.ToString());

There has to be a more efficient way to do this?

2

There are 2 answers

3
Jon Skeet On BEST ANSWER

If you're happy to convert it into LINQ to XML, it's really simple:

XDocument original = ...; // However you load the original document
// Separated out for clarity - could be inlined, of course
string xpath = "//Person[not(PersonID = following::Person/PersonID)]"

XDocument people = new XDocument(
    new XElement("Persons",
        original.XPathSelectElements(xpath)
    )
);

You definitely don't need to convert each node into a string and back. You don't need to with XmlDocument either, but it wouldn't be quite as simple as with LINQ to XML :)

4
Martin Honnen On

With XmlDocument you can use

XmlDocument doc2 = new XmlDocument();
doc2.AppendChild(doc2.CreateElement("Persons"));
foreach (XmlElement person in allItems)
{
  doc2.DocumentElement.AppendChild(doc2.ImportNode(person, true));
}