10

One of the areas where I would like to see the use of dynamic is XML. I think it would make XML processing code simpler to write and I believe I saw some examples on that before C# 4 came out and it's mentioned in this answer as one of the uses of this feature.

So, my question is this: why wasn't the XmlDocument (or XDocument) made dynamic, or why isn't there some new class for dynamic XML manipulation in C# 4?

This is even more strange to me, when I consider that in PowerShell, XmlDocument is dynamic, code like $xmlDoc.root.subnode.subsubnode works there.

Community
  • 1
  • 1
svick
  • 236,525
  • 50
  • 385
  • 514
  • 5
    If any type would be changed, it would be XDocument, not XmlDocument. – John Saunders Jun 23 '10 at 01:26
  • Also, is the answer to this question specific to C#? – John Saunders Jun 23 '10 at 01:26
  • 2
    AFAIK, `dynamic` is a feature of C#, so yeah, specific to C#. – svick Jun 23 '10 at 01:41
  • 1
    Personally, I'd keep it "static". That's what XPath is for... as for PowerShell, well, it uses loads of reflection magic to make [most] *everything* "dynamic". –  Jun 23 '10 at 04:27
  • @pst nobody would be forcing you to use it, but it would be nice to have it as an option. – svick Jun 23 '10 at 10:18
  • The `dynamic` keyword is specific to C#, but the same feature is also exposed in VB directly, in F# via PowerPack, and (obviously) in IronPython, IronRuby, and any other language using the DLR. In all those cases, "making the object dynamic" is done in the same way. – Pavel Minaev Jul 08 '10 at 01:43
  • 1
    How would you reference elements with names containing a dash (e.g. ``) or dot (e.g. ``) that way? Also, how would you distinguish between a child attribute `x`, and a child element `x`, if both are present at the same time? – Pavel Minaev Jul 08 '10 at 01:51

4 Answers4

12

I'm surprised at the amount of seemingly authoritative discussion without an answer. Your question is FANTASTIC. It addresses EXACTLY the kind of awesome things the dynamic keyword was intended for. The trouble is, not a lot of people really know how to use it to its fullest.

While MS didn't build the dynamic XML objects for us, they did give us the tools to do it ourselves with the DynamicObject class. Here's one way to do what you're asking for with the old XmlDocument class.

public class DynamicXmlElement : DynamicObject {
   XmlElement _xmlEl;

   public DynamicXmlElement(string xml) {
      var xmldoc = new XmlDocument();
      xmldoc.LoadXml(xml);
      _xmlEl = xmldoc.DocumentElement;
   }

   public DynamicXmlElement(XmlElement el) {
      _xmlEl = el;
   }

   public override bool TrySetMember(SetMemberBinder binder, object value) {
      return false;
   }

   public override bool TryGetMember(GetMemberBinder binder, out object result) {
      XmlElement el = (XmlElement)_xmlEl.SelectSingleNode(binder.Name);
      if (el != null) {
         // wrap the element we found in a new DynamicXmlElement object
         result = new DynamicXmlElement(el);
         return true;
      }
      else if (binder.Name == "root") {
         // special case for handling references to "root"
         result = new DynamicXmlElement(_xmlEl.OwnerDocument.DocumentElement);
         return true;
      }
      else {
         // feel free to change this to prevent having accidental null reference issues
         // by just setting the result to a DynamicXmlElement with a null element and 
         // handling _xmlEl == null at the start of this method
         result = null;
         return false;
      }
   }

   public override string ToString() {
      return _xmlEl.InnerText;
   }
}

And here's how you would call the code. Note that this compiles in C# 4.0 only.

namespace ConsoleApplication4 {
   class Program {
      static void Main(string[] args) {
         var xmlstr = "<r><subnode><subsubnode>ABCs of dynamic classes</subsubnode></subnode></r>";
         dynamic xml = new DynamicXmlElement(xmlstr);
         Console.WriteLine(xml.subnode.root.subnode.subsubnode); // take the long way around...
         Console.ReadKey(true);
      }
   }
}

I can't take all the credit for this. Bamboo wrote this code for Boo back in 2003. C# has slowly been getting the features that Boo has had in .NET for years... first type inference, and now the IQuackFu style DynamicObject. Once they implement language macros so that you can make DSLs, I think they'll have caught up.

I'll leave writing the newer style XElement version of this code to the reader.

mattmc3
  • 17,595
  • 7
  • 83
  • 103
  • 1
    "It addresses EXACTLY what the dynamic keyword was intended for" - what the `dynamic` keyword was _intended_ for is something only the designers of the language, and the related part of the runtime library, can authoritatively answer. In this case, it was repeatedly stated that the #1 use case for `dynamic` is in fact working with Office APIs, and #2 is working with objects exposed from dynamic languages. This isn't to say that there aren't other valid uses, but me thinks the "intended for" claim is a tad too assertive. – Pavel Minaev Jul 08 '10 at 01:45
  • 1
    Fair enough. I suppose I should have said "It addresses what the DynamicObject class in conjunction with the dynamic keyword was MOST LIKELY intended for". Even the bloggers over at Microsoft are using it for this very purpose: http://blogs.msdn.com/b/csharpfaq/archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx – mattmc3 Jul 08 '10 at 01:59
6

Given some of the comments, let me provide an alternative answer. The original question asked why XmlDocument was not dynamic in .NET 4. While it may be possible to add "expando" property capability to the existing xml document classes via IDynamicMetaObjectProvider, doing so is likely a non-trivial endeavor. Making the original Xml object model from System.Xml fully dynamic would require some extensive modification of the Xml framework, and would require that IDynamicMetaObjectProvider be added to every object that is involved. That includes XmlDocument, XmlElement, XmlAttribute, XmlNode, and all of the other xml content types like comments, text nodes, etc. In addition, a considerable amount of support infrastructure, internal types, etc. that are involved in the lookup and processing of elements and attributes and values would also need to be modified (open up Reflector, and take a look at System.Xml...more than half of the types are internal, and they are all highly interdependent with each other and the available public types.)

It is also important to consider the proper scope of implementing expando properties for Xml in .NET. Would you stop at just the XmlDocument and related types? Or would it be more appropriate to include XPath, Xml Schema, etc.?

To answer the original question, "Why isn’t XmlDocument dynamic in .NET 4?", I think the simple answer is this: Implementing fully 'dynamic' API's, or in the case of Xml here, API's that provide property expansion of arbitrary xml documents, is far from a trivial task. Given Microsoft's work ethic, it makes logical sense that they would not approach such a task lightly, and if they attempt to implement expando properties for the Xml framework, I would hope and expect it to be done with the same level of careful attention and care they give to the rest of .NET.

jrista
  • 32,447
  • 15
  • 90
  • 130
  • 1
    Interesting answer, but my question is, why isn't there any type that could handle the `xmlDoc.root.subnode.subsubnode` syntax in C#? (Probably by implementing `IDynamicMetaObjectProvider`, if I understand it correctly.) – svick Jul 05 '10 at 10:27
  • But you can do that in C# 4, you can manage member lookup by yourself, see e.g. http://channel9.msdn.com/posts/RobBagby/deCast-Dynamic-Xml-with-C-40/. – svick Jul 06 '10 at 19:17
  • Yes, objects can be truly dynamic. See `DynamicObject` and `ExpandoObject` classes on MSDN, and the interfaces they implement. As such, the answer is effectively incorrect, and it is quite possible for e.g. `XmlDocument` to add the ability to be accessed as `((dynamic)root).foo.bar.baz` in future releases. – Pavel Minaev Jul 08 '10 at 01:47
  • @Pavel: Sure, I've read about DynamicObject and ExpandoObject. However, it would be considerably more difficult to make an XmlDocument truly navigable via runtime-dynamic "expando" properties than simply adding IDynamicMetaObjectProvider to XmlDocument. You would need to add 'dynamic' capability to every xml object in the tree, such as elements, attributes, text nodes, etc. I guess I see it more of a way of implementing sugary syntax translation than dynamic objects. The type itself is still concrete, although I guess you could add property-style accessors for an Xml document. – jrista Jul 08 '10 at 04:46
  • @svick: I did view that video, and I have also read a few articles on implementing expando properteis with IDynamicMetaObjectProvider in .NET. In most (perhapse all) of the examples I have seen so far seemed to be rather specific to the application at hand. I gave it some thought, and updated my answer to more directly answer your original question. It occurred to me that supporting property expansion of an xml document is far from a trivial task...and riddled with issues. – jrista Jul 08 '10 at 17:39
  • As a simple example...how does one expand this: JonRista. Using expando properties via `IDynamicMetaObjectProvider` on an XmlDocument...the resulting code to retrieve my first name would be: xmlDoc.root.person-info.first-name. Despite the fact that those element names are perfectly valid XML identifiers...they are invalid in the context of C# (and many other languages.) Reference: http://www.w3.org/TR/2000/REC-xml-20001006#NT-Nmtoken – jrista Jul 08 '10 at 17:44
-1

Most probably they haven't thought about it, and, programmers are usually not trigger-happy with putting dynamics all over the place: dynamic is a time-bomb. Catch as much as you can at compile time...

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
  • 1
    The thing is, you never know what XML you will get and whether it will be what you expect. And if it's not, you have to handle that at runtime, whether you use dynymics or not. – svick Jul 05 '10 at 10:38
  • @svick: that's what schema validation is for. – Ben M Jul 09 '10 at 03:28
-2

Because XmlDocument predates genereics and fixing it would break old code.

Also, XmlDocument is listed as semi-obsolete.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 3
    How would it break existing code if a class supported a new interface that wasn't there when the code was written? – Niki Jul 04 '10 at 19:58