Using XML Schema Import and Export for XmlSerializer
In a previous post, I outlined how you could import and export the XML schema for a type that you’re serializing with DataContractSerializer. Here’s how to do the same thing if you’re serializing objects with XmlSerializer:
static void RoundTripXmlMetadata(Type type)
{
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
//Import the type as an XML mapping
XmlTypeMapping mapping = new XmlReflectionImporter().ImportTypeMapping(type);
//Export the XML mapping into schemas
exporter.ExportTypeMapping(mapping);
//Print out the schemas
foreach (object schema in schemas)
{
((XmlSchema)schema).Write(Console.Out);
Console.WriteLine("\n");
}
Console.WriteLine("-------------------------------------------");
//Compile the schemas into one logical schema
schemas.Compile((o, args) => Console.WriteLine(args.Message), true);
XmlSchemaImporter importer = new XmlSchemaImporter(schemas);
//Import the schema element back into an XML mapping
mapping = importer.ImportTypeMapping(new XmlQualifiedName(mapping.ElementName, mapping.Namespace));
//Create a CompileUnit and CodeNamespace to contain the generated code
CodeCompileUnit compileUnit = new CodeCompileUnit();
CodeNamespace compileNs = new CodeNamespace();
compileUnit.Namespaces.Add(compileNs);
XmlCodeExporter codeGenerator = new XmlCodeExporter(compileNs);
//Export the XML mapping into code
codeGenerator.ExportTypeMapping(mapping);
//Generate C# code for the compile unit and print it out
CSharpCodeProvider provider = new CSharpCodeProvider();
provider.GenerateCodeFromCompileUnit(compileUnit, Console.Out, null);
}
I’ve tried to add comments to explain what’s happening. Essentially, we go through the following process when generating the schemas:
CLR type => XML mapping => Xml Schemas
and the following process when generating code:
Xml Schemas => XML mapping => C# code
And when we try running the type Dog defined below
public class Animal
{
public int age = 4;
public string name = "Rusty";
}
public class Dog : Animal
{
public DogBreed breed = DogBreed.LabradorRetriever;
}
public enum DogBreed {
GermanShepherd,
LabradorRetriever
}
Through the method, we get the following output:
<?xml version="1.0" encoding="IBM437"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="https://www.w3.org/2001/XMLSc
hema">
<xs:element name="Dog" nillable="true" type="Dog" />
<xs:complexType name="Dog">
<xs:complexContent mixed="false">
<xs:extension base="Animal">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="breed" type="DogBreed" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Animal">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="age" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" name="name" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DogBreed">
<xs:restriction base="xs:string">
<xs:enumeration value="GermanShepherd" />
<xs:enumeration value="LabradorRetriever" />
</xs:restriction>
</xs:simpleType>
</xs:schema>
-------------------------------------------
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.1
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Serialization", "0.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class Dog : Animal {
private DogBreed breedField;
/// <remarks/>
public DogBreed breed {
get {
return this.breedField;
}
set {
this.breedField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Serialization", "0.0.0.0")]
[System.SerializableAttribute()]
public enum DogBreed {
/// <remarks/>
GermanShepherd,
/// <remarks/>
LabradorRetriever,
}
/// <remarks/>
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Dog))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Serialization", "0.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class Animal {
private int ageField;
private string nameField;
/// <remarks/>
public int age {
get {
return this.ageField;
}
set {
this.ageField = value;
}
}
/// <remarks/>
public string name {
get {
return this.nameField;
}
set {
this.nameField = value;
}
}
}
Notice that the generated types look very similar to the types we started out with. In fact, as far as XmlSerializer is concerned, those types are equivalent. If you create an instance of your original class and an instance of the roundtripped class with the same values, you’ll get exactly the same XML output on the wire. That’s the guarantee that metadata roundtrips provide.