Wednesday, April 20, 2011

C# 4 Dynamics and XML

I've seen a couple of cool libraries for using dynamics to magically access your JSON. I knew similar things must be available for XML, and went looking. At this point, my usage is focused on read-only access to xml, of varying styles. Values may arbitrarily be in attributes or child nodes.

The two that I found that and decided to evaluate were Anoop Madhusudanan's ElasticObject and Aaron Powell's Dynamics library.

I set up a test application looking to evaluate performance and syntax. I accessed a handful of nodes, including one array of nodes and it's children. For the hand-built version, I set up a simple set of objects with simple properties and wrote code to extract values from XElements.

I had a bit of trouble with the API for the Dynamics library, but I saw some performance promise, so I went ahead to make modifications and support my needs. I went ahead and forked the project on bitbucket to share my changes here.

For the performance check, my Hand-Built code ended up at 6 milliseconds, Dynamics at 75 ms, and Elastic at 237 milliseconds (I pre-loaded the xml into an XElement and timed the parsing). So, the tradeoff here is time spent coding vs runtime. I don't expect my real usage to have a significant impact on the user's experience, so I'm ok with Dynamic's 12x slowdown for now. If it becomes a problem, I'll have a nice bottleneck to remove for version 2 :).

Here are some syntax comparisons:
Attribute Access
Hand-Built
if (t.MizConfig.InstrumentPlatform != "TC")
ElasticObject
if (t.MizConfig.InstrumentPlatform != "TC")
Dynamics plus mod Multi-level nesting was not possible as written, MizConfig was returning a string. Changed code to allow
if (t.MizConfig.InstrumentPlatform != "TC")
Iterating multiple same-named elements
Hand-Built
foreach (var ch in t.MizConfig.HWChannels)
ElasticObject
foreach (var ch in t.MizConfig["HWChannel"])
Dynamics
foreach (var ch in t.MizConfig.HWChannels)
Accessing Value (non-iterator) as a double. Note: hand-built is able to hide extraneous nodes from me
Hand-Built
d = t.PusherConfiguration.MaxForwardSpeed;
ElasticObject
d = double.Parse(~t.PusherConfiguration.Speeds.Record_Speed.Max);
Dynamics
d = double.Parse(t.PusherConfiguration.Speeds.Record_Speed.Max.Value);
Dynamics plus mod
d = t.PusherConfiguration.Speeds.Record_Speed.Max.To();
Conditionally dealing with optional children
Hand-Built
if(xelement.Element("YoMama") != null)

during parsing
ElasticObject
if (~dyn.MizConfig.YoMama != null)
Dynamics plus mod Didn't find an easy was as implemented, probably would have had to do dyn.MizConfig.Element.Element("YoMama") mixing in XElement access. Instead, I modified the code to allow
if (dyn.MizConfig.YoMama != null)

No comments:

Post a Comment