16

I am parsing a big number of big files and after profiling my bottleneck is:

XmlDocument doc = new XmlDocument();
doc.Load(filename);

This approach was very handy because I could extract nodes like this:

XmlNodeList nodeList = doc.SelectNodes("myXPath");

I am switching to XmlReader, but When I find the element I need to extract I am stuck with regards to how to build a XmlNode from it as not too familiar with XmlReader:

XmlReader xmlReader = XmlReader.Create(fileName);

while (xmlReader.Read())
{
   //keep reading until we see my element
   if (xmlReader.Name.Equals("myElementName") && (xmlReader.NodeType == XmlNodeType.Element))
   {
       // How do I get the Xml element from the reader here?
   }
}

I'd like to be able to build a List<XmlNode> object. I am on .NET 2.0.

Any help appreciated!

6 Answers 6

20

Why not just do the following?

XmlDocument doc = new XmlDocument();
XmlNode node = doc.ReadNode(reader);
Sign up to request clarification or add additional context in comments.

2 Comments

This is the correct answer as the other one leaves blank nodes!
7

The XmlNode type does not have a public constructor, so you cannot create them on your own. You will need to have an XmlDocument that you can use to create them:

XmlDocument doc = new XmlDocument();
while (xmlReader.Read())
{
    //keep reading until we see my element
    if (xmlReader.Name.Equals("myElementName") && (xmlReader.NodeType == XmlNodeType.Element))
    {
        // How do I get the Xml element from the reader here?
        XmlNode myNode = doc.CreateNode(XmlNodeType.Element, xmlReader.Name, "");
        nodeList.Add(myNode);
    }        
}

4 Comments

it seems to be creating empty nodes?
Yes, unless you add anything to the elements (by assigning something to the InnerText property for instance) they will be empty.
oh yep - looks obvious now since I am just passing element name in, thanks
This results in only empty nodes. You can use doc.ReadNode(reader) to actually get the entire node as XmlNode
6

XmlReader and XmlDocument have a very distinct way of processing. XmlReader keeps nothing in memory and uses a forward-only approach as opposed to building a full DOM tree in memory for XmlDocument. It is helpful when performance is an issue, but it also requires you to write your application differently: instead of using XmlNode, you don't keep anything and only process "on the go": i.e., when an element passes by that you need, you do something. This is close to the SAX approach, but without the callback model.

The answer to "how to get the XmlElement" is: you'll have to build them from scratch based on the info from the reader. This, unfortunately, defies the performance gain. It is often better to prevent using DOM approaches altogether once you switch to XmlReader, unless for a few distinct cases.

Also, the "very handy" way to extract nodes using XPath (SelectNodes is what you show above) cannot be used here: XPath requires a DOM tree. Consider this approach a filtering approach: you can add filters to the XmlReader and tell it to skip certain nodes or read until a certain node. This is extremely fast, but a different way of thinking.

Comments

4

Use XmlDocument.ReadNode for this approach. Put XmlReader in using statement and use XmlReader.LocalName instead of Name to remove namespace prefix.

Comments

1

I've used the following workaround when I've had to insert data from a XmlReader into a XmlDocumenht:

XmlReader rdr = cmd.ExecuteXmlReader();

XmlDocument doc = new XmlDocument();

// create a container node for our resultset
XmlElement root = doc.CreateElement("QueryRoot");
doc.AppendChild(root);

StringBuilder xmlBody = new StringBuilder();

while(rdr.Read())
{
    xmlBody.Append(rdr.ReadOuterXml());
}

root.InnerXml = xmlBody.ToString();

Comments

1

Here is my approach:

public static IEnumerable<XmlNode> StreamNodes(
    string path,
    string[] tagNames) 
{            
    var doc = new XmlDocument();            
    using (XmlReader xr = XmlReader.Create(path)) 
    {
        xr.MoveToContent();
        while (true) {
            if (xr.NodeType == XmlNodeType.Element &&
                tagNames.Contains(xr.Name)) 
            {
                var node = doc.ReadNode(xr);
                yield return node;
            } 
            else 
            {
                if (!xr.Read()) 
                {
                    break;
                }
            }
        }
        xr.Close();
    }                        
}
// Used like this:
foreach (var el in StreamNodes("orders.xml", new string[]{"order"})) 
{
    ....
}

The nodes can then be imported into another document for further processing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.