views:

68

answers:

1

Is there a DLR-enabled XML navigation and reading class available in .NET 4.0? For example, imagine I have this XML:

<foo>
   <bar>foobar is here</bar>
   <bar>foobar is also here</bar>
   <baz>foobar is not here</bar>
</foo>

Is there an easy way to navigate through this XML like this:

var doc = SomeDlrEnabledParser.Parse (xmlString);
foreach (var node in doc.foo.bar)
{
    if (node == "foobar is here")
        DoSomething();
    else
        DoSomethingElse();
}

I can see lots of reasons why the approach above would be problematic, including namespaces, attributes vs. elements, differentiating collections vs. single elements, encoded XML vs text, etc.

But much of the XML I deal with is very simple and read-only, and I'd be willing to accept sensible default behaviors in exchange for avoiding the "parentheses and quote soup" that's characteristic of working with simple XML in a pre-4.0 world.

For example, the "dot" operator could check attribute names before subelement names. Or non-collection operations would be automatically applied to the first element (like jQuery does).

Does the .NET 4.0 Framework Class Library contain anything like this? if not, any recommendations for a good open-source project or sample of a DLR-enabled XML library?

A: 

I did a little test implementation:

public class DynamicXml : DynamicObject, IEnumerable<XNode>
{
    private readonly IEnumerable<XNode> nodes;

    public DynamicXml(params XNode[] nodes)
    {
        this.nodes = nodes;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var children = nodes.OfType<XContainer>().SelectMany(node => node.Elements(binder.Name)).Cast<XNode>().ToArray();
        result = new DynamicXml(children);
        return true;
    }

    public IEnumerator<XNode> GetEnumerator()
    {
        return nodes.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

And example:

class Program
{
    static void Main(string[] args)
    {
        dynamic dynDoc = new DynamicXml(XDocument.Parse(
            @"<foo>
                <bar>foobar is here</bar>
                <bar>foobar is also here</bar>
                <baz>foobar is not here</baz>
              </foo>"));

        foreach (XElement node in dynDoc.foo.bar)
        {
            if (node.Value == "foobar is here")
                Console.WriteLine("found: {0}", node);
            else
                Console.WriteLine("not found: {0}", node);
        }

        Console.ReadKey(true);
    }
}

However, it seems that the interface between this and some other code is not so nice.

For example, if we wanted to use Linq against the nodes we must first explicitly cast to an IEnumerable... otherwise our Where will be interpreted as an element name.

((DynamicXml)dynXml.foo.bar).Where(x => x.Value == "foobar is here");

You can implement Where directly on the dynamic type, but you must then qualify all lambdas like this:

foo.bar.Where((Func<XElement,bool>)(x => x.Value == "foobar is here")) 

That said, if you're just extracting values from an XML tree like this, I think it works okay.

Porges