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:

[System.Xml.Serialization.XmlElement()]
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…

[System.Xml.Serialization.XmlElement("key")]
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

[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable = false)]
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:

[System.Xml.Serialization.XmlArrayItemAttribute(ElementName = "key", IsNullable = false)]

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.XmlArray("keys")]
[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.XmlArray("keys")]
[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…

 

6 Comments

  • Thx for the article! Well written, with lots of those helpful code examples that we codesmiths like to see. I ran across it doing research on the xsd.exe tool.

    You mentioned this: "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)."

    Have you tried using a call to xsd.exe in the project's pre-build event? I haven't tried this myself, but I have builder project that just builds my assemblies which does several code transformations before performing the build. Something to try, anyway.

  • Yes, I did... however it didn't work as I wanted it to. The problem is for run #1... if you attempt to do it via pre-build, then the files are not added to your project and are therefore not included in the compile action... so XSD.exe will generate the C# file(s) however they will not be compiled into the resultant assembly. My current research on this topic leads me to the conclusion that the "most right" answer is to develop a Single File Generator add-on to VS2005 which would provide this functionality... the only task you would have to do would be to set the custom tool for your XSD file to the name of the single file generator.

  • Trying to use XSD and works fine in examples but not in real world. Just keep getting 'the type or namespace name 'mytableadapter' could not be found.
    I have moved the xsd reluctantly to the app_code directory from a folder called datalayer, and trying to reference it from a businesslayer folder. Suely that doesn't have to be in the appcode folder does it?

  • >Yes, I did... however it didn't work as I wanted it to.

    The thing to do is to first compile .xsd file from VS command line using xsd.exe. Next you add genereated file to the project, this ensuring it will be build. And finally you add pre-build event to run xsd.exe before each build (same ou have done in step 1).

  • Try using the DataContract wizard that is part of ThinkTectures WCF tool. This can be downloaded free from www.thinktecture.com. Once installed into Visual Studio you can create the C# code without in situ. It also makes a much better job of creating the code and does not create those horrid long atttributes names so loved by XSD.EXE. It will also create generic lists for any array type defined in the schema. Later on you can investigate the web services generation facilities.

  • Obviously you are doing something wrong if you have to make so many changes to the file after it is generated. I would go back and review your procedures and how your coding is completed. You are having to make too many changes to something that is so somple

Comments have been disabled for this content.