Deanna
Deanna

Reputation: 24283

XmlReader best practices

I've had a good read through MSDN and the XmlReader related questions on StackOverflow and I haven't yet come across a decent "best practices" example.

I've tried various combinations and each seems to have downsides, but the best I can come up with is as follows:

The XML:

<properties>
  <actions:name>start</actions:name>
  <actions:value type="System.DateTime">06/08/2011 01:26:49</actions:value>
</properties>

The code:

// Reads past the initial/root element
reader.ReadStartElement();

// Check we haven't hit the end
while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement) {
    if (reader.IsStartElement("name", NamespaceUri)) {
        // Read the name element
        this.name = reader.ReadElementContentAsString();
    } else if (reader.IsStartElement("value", NamespaceUri)) {
        // Read the value element
        string valueTypeName = reader["type"] ?? typeof(string).Name;
        Type valueType = Type.GetType(valueTypeName);
        string valueString = reader.ReadElementContentAsString();
        // Other stuff here that doesn;t matter to the XML parsing
    } else {
        // We can't do anything with this node so skip over it
        reader.Read();
    }
}

This is being passed into my class from a .ReadSubTree() call and each class reads its own information. I would prefer it NOT to rely on it being in a specific order.

Before this, I did try several variations.

1) while(reader.Read()) This was taken from various example, but found that it "missed" some elements when .ReadContent*() of element 1 left it on the start of the element 2, .Read read over it to element 3.

2) Removing the .Read() caused it to just get stuck after the first element I read.

3) Several others I long consigned to "failed".

As far as I can see, the code I've settled on seems to be the most accepting and stable but is there anything obvious I'm missing?

(Note the c# 2.0 tag so LINQ/XNode/XElement aren't options)

Upvotes: 1

Views: 1829

Answers (1)

Kiran
Kiran

Reputation: 376

One approach is to use a custom XmlReader. XmlReader is abstract and XmlReaders can be chained, giving a powerful mechanism to do some domain specific processing in a reader.

Example: XamlXmlReader

Help on XmlWrappingReader

Here's a sample of how it could be implemented (See inline comments):

/// <summary>
/// Depending on the complexity of the Xml structure, a complex statemachine could be required here.
/// Such a reader nicely separates the structure of the Xml from the business logic dependent on the data in the Xml. 
/// </summary>
public class CustomXmlReader: XmlWrappingReader
{
    public CustomXmlReader(XmlReader xmlReader)
        :base(XmlReader.Create(xmlReader, xmlReader.Settings))
    {

    }

    public override bool Read()
    {
        var b = base.Read();
        if (!b)
            return false;
        _myEnum = MyEnum.None;
        if("name".Equals(this.Name))
        {
            _myEnum = MyEnum.Name;
            //custom logic to read the entire element and set the enum, name and any other properties relevant to your domain
            //Use base.Read() until you've read the complete "logical" chunk of Xml. The "logical" chunk could be more than a element.
        }
        if("value".Equals(this.Value))
        {
            _myEnum = Xml.MyEnum.Value;
            //custom logic to read the entire element and set the enum, value and and any other properties relevant to your domain
            //Use base.Read() until you've read the complete "logical" chunk of Xml. The "logical" chunk could be more than a element.
        }
        return true;
    }
    //These properties could return some domain specific values
    #region domain specific reader properties. 
    private MyEnum _myEnum;
    public MyEnum MyEnum
    {
        get { return _myEnum; }
    }

    #endregion
}

public enum MyEnum
{
    Name,
    Value,
    None
}

public class MyBusinessAppClass
{
    public void DoSomething(XmlReader passedInReader)
    {

        var myReader = new CustomXmlReader(passedInReader);
         while(myReader.Read())
         {
             switch(myReader.MyEnum)
             {
                 case MyEnum.Name:
                     //Do something here;
                     break;
                 case MyEnum.Value:
                     //Do something here;
                     break;

             }
         }
    }
}

A word of caution : This might be over engineering for some simple Xml processing that you've shown here. Unless, you have more that two elements that need custom processing, this approach is not advised.

Upvotes: 1

Related Questions