XML-to-C# Code Generation

by Jon Davis 1. May 2007 23:16

Altova probably hates me. Not their products, but the company. I've frequently wanted to give their product line a noble shot for utilization, but I never have time to give it a fair shot, so I am never able to afford to purchase it or give it a full recommendation to my employer. My old user ID shows up in their tutorial videos alongside generic examples of hackers and spammers. For years, I'd try reinstalling the product to get past the 30-day trial in hopes that I'd have time to really check their cool tools out. When they killed that ability, I tried doing it within a virtual machine. Now in VMs I cannot get a trial key anymore; perhaps my e-mail domain name is blocked.

But I often forget that there is no real need for an investment in some third party XML code generation tool like Altova's XMLSpy or MapForce if you need a complete object model written in C# to introspect a deserialized XML file. After spending hours Googling for C# code generators from XML, I realized that the solution is right under my nose. And I don't have to spend a dime for it.

Why Generate?

You might be asking, why are you trying to generate C# code? Doesn't System.Xml.XmlDocument and its XPath support work well enough to do what you need to do with an XML document? The answer is, yes, sometimes. Sometimes Notepad.exe is sufficient to edit an .aspx file, too, but that doesn't mean that having a good ASP.NET IDE w/ code generation, like Visual Studio, should be ignored for Notepad.

In fact, I was happy with using XmlDocument until I realized that some of the code I was tasked to maintain consisted of hundreds of lines of code that would read CDATA values into a business object's own properties, like this:

XmlNode node = storyNode.SelectSingleNode("./title");
if (node != null && node.ChildNodes.Count > 0 && node.ChildNodes[0].value != null)
{
	this._title = node.ChildNodes[0].Value
}

node = storyNode.SelectSingleNode("./category");
if (node != null && node.ChildNodes.Count > 0 && node.ChildNodes[0].value != null)
{
	this._category = node.ChildNodes[0].Value
}

...

This just seemed silly to me. When I started working with a whole new XML schema that was even more complex, I decided that manually writing all that code is just ludicrous.

XML -> XSD

Visual Studio 2005 (of which there are freely downloadable Express versions, of course) has the ability to introspect an XML document to generate an XML Schema (.xsd). It's really very simple: load the XML file into the IDE, then select "Create Schema" from the "XML" menu. Overwhelmed by the complexity of it all yet?

Bear in mind that the resulting Schema is not perfect. It must be validated--by you. If at first glance the schema looks fine, there's a simple test to validate it: simply programmatically load your XML document while enforcing the schema. For my purposes, I found that most of the adjustments I needed to make were just to make "required" elements "optional", unless of course they were indeed required.

XSD -> C# Code

If the schema's clean, all you need is the .NET Framwork SDK, which comes bundled with Visual Studio 2005. Tucked away therein is XSD.exe, which does the magic for you. All you have to do is pass it "/c" along with the name of the .xsd file and the new name of the .cs file you want it to auto-generate.

The generated C# code isn't always perfect, either. To say nothing of the rough comment stubs, one or two textual content elements were completely ignored in my case--the attributes were exposed as C# properties but the content, which was CDATA, was not. Easy enough to fix. This was likely due to an imperfect XSD file, but since this was really a run-once-and-forget-about-it effort, I was not afraid of diving into the C# to add the missing properties.

        private string _value;
        [System.Xml.Serialization.XmlText()]
        public string value
        {
            get { return _value; }
            set { _value = value; }
        }

System.Xml.Serialization.XmlSerializer works flawlessly with the generated C# code. I created the following generic class for the generated classes to inherit, so that they automatically offer a Deserialize() method:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace MyProject.XmlGen
{
    public class XmlDeserializer<T>
    {
        public static T Deserialize(string xmlFilePath)
        {
            using (FileStream stream = new FileStream(xmlFilePath, FileMode.Open))
            {
                return Deserialize(stream);
            }
        }
        public static T Deserialize(Stream xmlFileStream)
        {
            return (T)Serializer(typeof(T)).Deserialize(xmlFileStream);
        }

        public static T Deserialize(TextReader textReader)
        {
            return (T)Serializer(typeof(T)).Deserialize(textReader);
        }

        public static T Deserialize(XmlReader xmlReader)
        {
            return (T)Serializer(typeof(T)).Deserialize(xmlReader);
        }

        public static T Deserialize(XmlReader xmlReader, string encodingStyle)
        {
            return (T)Serializer(typeof(T)).Deserialize(xmlReader, encodingStyle);
        }

        public static T Deserialize(XmlReader xmlReader, XmlDeserializationEvents events)
        {
            return (T)Serializer(typeof(T)).Deserialize(xmlReader, events);
        }

        public static T Deserialize(XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
        {
            return (T)Serializer(typeof(T)).Deserialize(xmlReader, encodingStyle, events);
        }

        private static XmlSerializer _Serializer = null;
        private static XmlSerializer Serializer(Type t)
        {
            if (_Serializer == null) _Serializer = new XmlSerializer(t);
            return _Serializer;
        }

    }
}

So with this I just declare my generated C# as such:

public class MyGeneratedClass : XmlDeserializer<MyGeneratedClass>
{
 ...
}

Literally, now it takes a whopping ONE line of code to deserialize an XML file and access it as a complex object model.

MyGeneratedClass myObject = MyGeneratedClass.Deserialize(xmlFilePath);

Cheers.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , , ,

Software Development


 

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Jon Davis (aka "stimpy77") has been a programmer, developer, and consultant for web and Windows software solutions professionally since 1997, with experience ranging from OS and hardware support to DHTML programming to IIS/ASP web apps to Java network programming to Visual Basic applications to C# desktop apps.
 
Software in all forms is also his sole hobby, whether playing PC games or tinkering with programming them. "I was playing Defender on the Commodore 64," he reminisces, "when I decided at the age of 12 or so that I want to be a computer programmer when I grow up."

Jon was previously employed as a senior .NET developer at a very well-known Internet services company whom you're more likely than not to have directly done business with. However, this blog and all of jondavis.net have no affiliation with, and are not representative of, his former employer in any way.

Contact Me 


Tag cloud

Calendar

<<  December 2020  >>
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar