09 September 2005

XML extensibility, xsi:type, XmlSerializer and configuration (or how to leverage XmlSerializer + OO extensibility)

Daniel Cazzulino got this smashing discussion on XML extensibility, xsi:type, XmlSerializer and configuration (or how to leverage XmlSerializer + OO extensibility): "XmlSerializer ser = new XmlSerializer( typeof( People ), new Type[] { typeof( Employee ) } );"

To sum up, this is a pattern on leverage OO inheritance and XML extensibility.

We want to produce a schema like this (sample data)


<?xml version="1.0" encoding="utf-16" ?>
<Products xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://whatever.com/example/DataTypes.xsd">
<Vehicle xsi:type="Car" Price="20000" Fuel="Petrol" Chassis="Saloon">
<VIN>070121SD1T69079YFW</VIN>
<Brand>AUDI</Brand>
<Comfort>Climate Control, CD</Comfort>
<Safe>ABS, Twin Airbage</safe>
</Vehicle>
<Vehicle xsi:type="Tractor" Price="22000" Fuel="Diesel">
<VIN>01211334SD9079YFW</VIN>
<Brand>Famer's Friend</Brand>
<FieldTools>Wrench</FieldTools>
</Vehicle>
</Products>

the scehma:


namespace ClassLibrary1
{
using System.Xml;
using System.Xml.Serialization;

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://whatever.com/example/DataTypes.xsd")]
[XmlRootAttribute(Namespace="http://whatever.com/example/DataTypes.xsd", IsNullable=false)]
public class Products
{
[XmlElement("Vehicle", typeof(Vehicle))]
public Vehicle[] Item;
}

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://whatever.com/example/DataTypes.xsd")]
public class Vehicle
{
/// <summary>retail price</summary>
[XmlAttributeAttribute("Price")]
public virtual double Price
{
get { return _price; }
set { _price = value; }
}double _price;

/// <summary>Fuel Type</summary>
[XmlAttributeAttribute("Fuel")]
public virtual string Fuel
{
get { return _fuel; }
set { _fuel = value; }
}string _fuel;

/// <summary>Vihicle Identifer Number</summary>
[XmlElementAttribute("Vin")]
public virtual string Vin
{
get { return _vin;}
set { _vin = value;}
}string _vin;

/// <summary>Vihicle brand name</summary>
[XmlElementAttribute("Brand")]
public virtual string Brand
{
get { return _brand;}
set { _brand = value;}
}string _brand;

// make the Record xml schema extensible
[XmlAnyElement()]
public XmlElement[] AllElements;

[XmlAnyAttribute()]
public XmlAttribute[] AllAttributes;
}

[XmlTypeAttribute(Namespace="http://whatever.com/example/DataTypes.xsd" )]
[XmlRoot("Vehicle")]
public class Car : Vehicle
{
[XmlAttributeAttribute("Chassis")]
public string Chassis;
/// <summary>Vihicle brand name</summary>

[XmlElementAttribute("Comfort")]
public virtual string ComfortPack
{
get { return _comfortPack;}
set { _comfortPack = value;}
}string _comfortPack;

/// <summary>Vihicle brand name</summary>

[XmlElementAttribute("Safe")]
public virtual string SaftyDevice
{
get { return _saftyDevice; }
set { _saftyDevice = value; }
}string _saftyDevice;
}

public class Tractor : Vehicle
{
/// <summary>field tools included</summary>
[XmlElement("FieldTools")]
public virtual string FieldTools
{
get { return _fieldTools; }
set { _fieldTools = value; }
}string _fieldTools;
}
}

Now to serialize the following object, I created a Nunit Test case to run it, very handy.


using System;
using NUnit.Framework;

namespace ClassLibrary1 {
using System.IO;
using System.Xml;
using System.Xml.Serialization;

[TestFixture]
public class TestClass1 {

[SetUp]
public void SetUp() {}

[TearDown]
public void TearDown() {}

[Test] public void Test()
{
Car car1 = new Car();
car1.Chassis = "Saloon";
car1.ComfortPack = "Climate Control, CD";
car1.SaftyDevice = "ABS, Twin Airbage";
car1.Brand = "AUDI";
car1.Fuel = "Petrol";
car1.Price = 20000.00;
car1.Vin = "070121SD1T69079YFW";


Tractor tractor1 = new Tractor();
tractor1.Brand = "Famer's Friend";
tractor1.FieldTools = "Wrench";
tractor1.Fuel = "Diesel";
tractor1.Price = 22000;
tractor1.Vin = "01211334SD9079YFW";

Products p = new Products();
p.Item = new Vehicle[] {car1, tractor1} ;

//The most critical step, add the extended type to schema, so serializer knows what to to do
XmlSerializer serializer = new XmlSerializer( typeof( Products ), new Type[] {typeof(Car), typeof(Tractor)} );
StringWriter writer = new StringWriter();

serializer.Serialize(writer, p);
XmlDocument actual = new XmlDocument();
actual.LoadXml(writer.ToString());
Console.WriteLine(actual.OuterXml);
}
}
}

This should produce the sample data as shown above.

Question: what are AllElements, AllAttributes in Vichicle class for?

No comments: