Daniel Cazzulino's Blog : Customizing XSD->Classes code generation, the "easy" way

Subscriptions

News

Source code published in this blog is public domain unless otherwise specified.

 

kzu in LinkedIn

  Microsoft MVP Profile

 Contact

Post Categories

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

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

posted on Friday, October 24, 2003 2:28 PM by kzu