The Illusion of Strongly Typed XML with C#

This is essentially a redo of an earlier blog post of mine on how to create the illusion of a strongly typed xml in Boo using Duck Typing, except this time we’re using the dynamic keyword in C#.

http://cid-dfcd2d88d3fe101c.skydrive.live.com/embedrowdetail.aspx/blog/justnbusiness/XmlBuilder.cs

Here is an example of how it might look to use such a thing.

dynamic builder = new XmlBuilder();
builder.Main.One = "Hello";
builder.Main.Two = "World";

Console.WriteLine(builder.ToString());

Console.WriteLine(
    "{0} {1}!",
    builder.Main.One,
    builder.Main.Two);

builder = new XmlBuilder();
builder.Main["One"] = "Hello";
builder.Main["Two"] = "World";

Console.WriteLine(builder.ToString());

Console.WriteLine(
    "{0} {1}!",
    builder.Main["One"],
    builder.Main["Two"]);

XmlBuilder is a class I created myself, it inherits from DynamicObject and it intercepts TryGetMember, TrySetMember, TryGetIndex and TrySetIndex in order to allow you to navigate an internal XmlDocument object. Here is the output when the above is run.

<Main><One>Hello</One><Two>World</Two></Main>
Hello World!
<Main One=”Hello” Two=”World” />
Hello World!

Notice the use of properties corresponds to drilling into elements and the use of indexers corresponds to drilling into Attributes. This is just a proof of concept but you could see how this sort of thing would be useful.

Here is the complete XmlBuilder class that makes it all happen.

public class XmlBuilder : DynamicObject
{
    private Func<string, XmlElement> createElement;
    private Func<string, XmlAttribute> createAttribute;
    private XmlNode node;

    public XmlBuilder()
    {
        this.node = new XmlDocument();
        this.createElement = s => ((XmlDocument)this.node).CreateElement(s);
        this.createAttribute = s => ((XmlDocument)this.node).CreateAttribute(s);
    }

    private XmlBuilder(
        XmlNode node, 
        Func<string, XmlElement> createElement, 
        Func<string, XmlAttribute> createAttribute)
    {
        this.node = node;
        this.createElement = createElement;
        this.createAttribute = createAttribute;
    }

    public override bool  TryGetMember(GetMemberBinder binder, out object result)
    {
        XmlNode found = this.node[binder.Name];
        if (found == null)
        {
            found = this.createElement(binder.Name);
            this.node.AppendChild(found);
        }

        result = new XmlBuilder(found, this.createElement, this.createAttribute);
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        XmlNode found = this.node[binder.Name];
        if (found == null)
        {
            found = this.createElement(binder.Name);
            this.node.AppendChild(found);
        }

        found.InnerText = value.ToString();
        return true;
    }

    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        if (indexes.Length != 1)
        {
            throw new InvalidOperationException("You may only specify a single index.");
        }

        XmlNode found = null;
        if (indexes[0] is string)
        {
            string name = (string)indexes[0];
            found = this.node.Attributes[name];
            if (found == null)
            {
                found = this.createElement(name);
                this.node.AppendChild(found);
            }
        }
        else if (indexes[0] is int)
        {
            int index = (int)indexes[0];
            found = this.node.Attributes[index];
        }

        if (found != null)
        {
            result = new XmlBuilder(found, this.createElement, this.createAttribute);
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }

    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
    {
        if (indexes.Length != 1)
        {
            throw new InvalidOperationException("You may only specify a single index.");
        }

        XmlAttribute found = null;
        if (indexes[0] is string)
        {
            string name = (string)indexes[0];
            found = this.node.Attributes[name];
            if (found == null)
            {
                found = this.createAttribute(name);
                this.node.Attributes.Append(found);
            }
        }
        else if (indexes[0] is int)
        {
            int index = (int)indexes[0];
            found = this.node.Attributes[index];
        }

        if (found != null)
        {
            found.InnerText = value.ToString();
            return true;
        }
        else
        {
            return false;
        }
    }

    public override string ToString()
    {
        return this.node.InnerXml;
    }
}

Author: justinmchase

I'm a Software Developer from Minnesota.

1 thought on “The Illusion of Strongly Typed XML with C#”

  1. hey, this is a very useful information for my purspose of building Xml like Strongly Typed objects. Have you thought about how you can multiple nested elements using the XmlBuilder class above? Apparently when i try to create a nested element structure, it is now updating the element instead of creating a new element.

Drop a brain bomb

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s