Using XSD in your C# and MPS programming...
So I need to begin this post by admitting that I’m relatively new to the XSD world… particuarly as it applies to C#. While I’ve done a signficant amount of Xml programming and manipulation in C#, my recent forrays into this technology cause me to reconsider my belief that I’m an experienced Xml programmer…
Lately I’ve been working both with team members at my company as well as some colleagues from other companies on the topic of better and/or more consistent processing of the Xml used to passs data in and out of the MPF engine. It seems that we all tend to be very comfortable working in an OO C# environment both at the layer above MPF (presentation/application layer, etc.) as well as below the engine (providers). It seems that we are frequently performing the same operations (getting Xml nodes, performing validation steps, adding nodes to Xml documents, etc.)… one would think that there could be some more consistent/resuable fashion… and we did… various libraries of different sorts that we reused and extended on both sides of the engine.
I’ve been playing with an idea suggested to me by Mark which is to use XSD schemas to define the Xml “interfaces” and then, utilizing xsd.exe, generating C# serializable classes to represent those Xml blobs. Then I can utilize the serialization and de-serialization tools built into the .NET Framework to convert the C# objects into the input Xml stream as well as to deserialize the return back into object form for normal processing. Further, I can use the new validation features of a .NET 2.0 XmlTextReader (as compared to a 1.1 XmlValidatingReader) in conjunction with the Xsd file used to generate the class to validate Xml input from external sources.
For the most part, this approach is very interesting to me and I’ve been using it on a project recently and been increasingly pleased. There are, however, a few things that I’m not completely pleased with… some of which I’ve found work-arounds for and others that are still open issues.
- Documentation – One of the issues I ran into was a relative lack of documentation and online material on this topic (found plenty of Xsd information in the context of DataSets, but not much in the way of custom serialization and Xsd definitions which is why I’m writing this blog post (maybe it will be helpful to someone else looking for information on the topic). I should note that I did find a fair amount of information on Xsd integration and utilization but very few articles that discuss any .NET 2.0–specific features/issues.
- Auto Generation of Code – I was really hoping for some “magic” switch or property I could set on each of the Xsd files in VS.NET that would cause VS.NET to automatically generate the C# from the Xsd for me (maybe at compile time?) however, no such switch is to be found (at least not yet by me). Instead, I’ve been generating the XSD’s and then shelling out to a command prompt to use the xsd.exe command line tool.
- Inability to “Decorate” XSD – I have actually found work-arounds to the items below, however they involve manipulating the generated C# rather than modifying my XSD in some way to control the code generated by the output. While my current approach works, it leaves me open to problems if/when I need to regenerate my C# due to updates to my XSD.
- Casing – Another problem I ran into was that xsd.exe uses the element and attribute names from the schema to generate the names for the C# objects and properties. The *problem* with this is that most of my Xml nodes are camel-cased which results in C# class names that are camel-cased. This is inconsistent with our internal coding standards (as well as FXCop rules) which require them to be pascal-cased.
- Naming – for similar reasons as the previous item, my generated C# classes ended up with some pretty interesting names such as classes called keykey or keypropertytype. Probably more than the casing, it was important to me to “fix” this problem so that the resultant code was legible for the next person working on this codebase.
- Arrays vs. List<t> – Further complicating things is the fact that xsd.exe generates C# that uses strongly-typed arrays for collections of nodes. While these definitely work, they seem “klugy” to me as I’ve gotten rather used to Add() method of the ArrayList. Further, I’ve lately become very comfortably with strongly-typed lists (List<t>) in .NET 2.0 and really would prefer to use that programming construct instead.
I was able to work around the casing issue for switches by modifying the generated C# classes to match our coding standards and then decorating them so that the XmlSerializer knows which Xml nodes to algin with which classes. The xsd.exe tool initially genrated code that looked like the following:
public key key
{
get { return this.keyField; }
set { this.keyField = value; }
}
I then updated the code and tweaked the decoration as follows to ge the casing the way I wanted…
public Key Key
{
get { return this.keyField; }
set { this.keyField = value; }
}
What stumped me for a few minutes was how to handle the casing of the parent node of a collection (i.e. a node named keys that has children named key). The compiler generated code that looks like
public key[] keys
{
get { return this.keysField; }
set { this.keysField = value; }
}
What I wanted, is a property called Keys (rather than keys). There were two tricks I found here. The first was to indicate the naming of the child nodes (“key”) which is accomplished by setting the ElementName property of the XmlArrayItemAttribute as follows:
The second step, is to decorate the property such that the the serializer knows that the Key[] array maps to the <keys/> node. This is accomplished by adding an additional decoration to the property of type XmlArray() and setting the constructor value. The following is the fully updated (at this stage) array property.
[System.Xml.Serialization.XmlArrayItemAttribute(ElementName = "key", IsNullable = false)]
public Keys[] Keys
{
get { return this.keysField; }
set { this.keysField = value; }
}
Next I set to work on the array issue… I really wanted to be using strongly-typed lists (List<t>) which I figured had to be possible. Come to find out, it is, and all you have to do is change the types (no futher decoration necessary) yeilding an updated signature as follows:
[System.Xml.Serialization.XmlArrayItemAttribute(ElementName = "key", IsNullable = false)]
public List<Key> Keys
{
get { return this.keysField; }
set { this.keysField = value; }
}
Finally, I wanted to address the naming issue… Looking at the steps I’d accomplished so far, I figured that the decorations simply allow me to disassociate the C# class/property name from the Xml tag name and it proved to be correct – in the same way that I was able to adjust for casing, I could adjust for naming changes.
At this point I had solved most of my problems so I continued working and will use it “as-is” for the time being… if only I could have decorated the XSD so that these changes would be implemented as the code was generated…