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.