Customizing XSD->Classes code generation, the "easy" way

Note: this entry has moved.

Update: check a more thorough explanation of this techique in Code Generation in the .NET Framework Using XML Schema article published in the MSDN XML DevCenter, and the companion post on the VS.NET custom tool for it.

I've always been disgusted by the imposibility to customize XSD.EXE tool. I've even thought about some workaround for the all-public-fields issue. However, I was WRONG. It's perfectly possible to generate fully customized classes from an XSD Schema, if not by calling XSD.EXE, by reusing the very same classes it uses.

This is achievable without any reflection hack! All public (althought certainly undocumented) classes and methods are used.

The "trick" involves using two key classes: XmlSchemaImporter and XmlCodeExporter, both from the System.Xml.Serialization namespace.

// Load the schema to process.
XmlSchema xsd = XmlSchema.Read( stm, null );

// Collection of schemas for the XmlSchemaImporter
XmlSchemas xsds = new XmlSchemas();
xsds.Add( xsd );
XmlSchemaImporter imp = new XmlSchemaImporter( xsds );

// System.CodeDom namespace for the XmlCodeExporter to put classes in
CodeNamespace ns = new CodeNamespace( "Generated" );
XmlCodeExporter exp = new XmlCodeExporter( ns );

// Iterate schema items (top-level elements only) and generate code for each
foreach ( XmlSchemaObject item in xsd.Items )
{
  if ( item is XmlSchemaElement )
  {
    // Import the mapping first
    XmlTypeMapping map = imp.ImportTypeMapping(
      new XmlQualifiedName( ( ( XmlSchemaElement ) item ).Name, 
      xsd.TargetNamespace ) );
    // Export the code finally
    exp.ExportTypeMapping( map );
  }
}

// Code generator to build code with.
ICodeGenerator generator = new CSharpCodeProvider().CreateGenerator();

// Generate untouched version
using ( StreamWriter sw = new StreamWriter( @"E:\Generated.Full.cs", false ) )
{
  generator.GenerateCodeFromNamespace(
    ns, sw, new CodeGeneratorOptions() );
}

The CodeNamespace variable ns contains a full CodeDom hierarchy with all the types that were generated. Therefore, we can easily customize their definitions by adding attributes, custom methods, etc. Even converting those annoying public fields to properties, which is now much more robust than the find-and-replace method I used on a previous life :):

+ FieldsToProperties method

Now, simply passing the namespace generated by the previous code will result in custom classes with properties instead of fields, with the appropriate XmlSerialization attributes as generated initially. Below is the customized complete schema for the Pubs database:

+ Pubs XSD schema customized.

+ Complete Pubs XSD

2 Comments

  • I had also written a complete tool, in this case to create Bamboo Persistence classes. However this may still do a decent job for you.



    Cheers

    Stephan

  • Fantastic! This is incredible. I'm glad to see you use the CodeDom for code generation. I sat down this morning with the intent to either find this solution, or write my own implementation. I'm glad I found you.



    I'm about to go check out your Sourceforge project since I'd like to write a CodeDom plugin that implements ISerializable in addition to the properties.

Comments have been disabled for this content.