Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
An XmlInclude attribute attached to a class will let the
serializer know about derived classes. The XmlSerializer cannot handle
situations where an object of a derived type occurs if a base type was declared
without some help from us, regardless whether the derived type occurs at the
root or somewhere else in the serialized object graph. The XmlSerializer needs
to know about types derived from a class at the time it does its class
analysis, i.e. when the constructor runs. If we do not explicitly declare the
derived type together with the base type the constructor is not able to locate
the derived type on its own and cannot create and process a type mapping for
the derived type.
Applying this attribute is very useful when you are developing
your own class library that maps to a hierarchy of XML schema types, where a
derived type can also replace a base type. Take the following XSD snippet for
example, which defines a base type Vehicle and two derived type Car and
Motorcycle:
Any element in this schema of type Vehicle could also contain a
Car or a Motorcycle type, because Car and Motorcycle are extensions of Vehicle
– just like we can replace a base class with a derived class in a class
hierarchy in an object-oriented programming environment. We can easily map this
hierarchy to a set of the .NET classes. We need a base class
Vehicle and two derived classes Car and Motorcycle, like the
ones in listing 9.4.
Listing 9.1: Vehicle class
hierarchy with the XmlInclude attribute
[XmlInclude(typeof(Car))]
[XmlInclude(typeof(Motorcycle))]
public class Vehicle
| #1
{
public Vehicle(){}
public string Make;
public string Model;
public int Year;
}
public class Car : Vehicle | #2
{
public Car() {}
public string VIN; | #3
}
public class Motorcycle : Vehicle | #4
{
public Motorcycle() {}
}
(annotation)
<#1 The base class of the hierarchy>
(annotation)
<#2 The first class derived from Vehicle>
(annotation)
<#3 The Car class adds an additional field>
(annotation)
<#4 Another class derived from
Vehicle>
We have to annotate the Vehicle class with an XmlInclude attribute for each of the
derived classes, if we want the XmlSerializer to be able to serialize Car and
Motorcycle objects where a Vehicle was declared.
NOTE: The XmlInclude attribute works recursively, i.e. the serializer
will also include types if the XmlInclude attribute was attached to a class
that was included because of a class that was already included through an XmlInclude
attribute.
With the XmlInclude attributes in place an XmlSerializer will know how
to serialize and deserialize Car and Motorcycle objects instead of a Vehicle
object, because the XmlSerializer found the XmlInclude attributes, analyzed the
types Car and Motorcycle and made a reference that each of those are valid
substitutes for the Vehicle type. To prove it we can create an XmlSerializer
for the Vehicle type and use it to serialize a Car object:
Car wifesCar = new Car();
…
XmlSerializer xs = new XmlSerializer(typeof(Vehicle));
xs.Serialize(xw, wifesCar);
The code doesn’t throw an exception, that’s already an indication
that everything went as expected.
Serialize() identified the type of the wifesCar object, found
a type mapping for the Car type and wrote the following XML to the output:
The name of the root
element indicates that the serialized object was declared to be of type Vehicle. In this case the XmlSerializer expects a Vehicle
object as the serialization root, because we passed Vehicle’s Type to the
constructor. The actual type of the serialized object is stored in an xsi:type
attribute, for deserialize to determine that the serialized object does not
conform to the declared type. The type attribute belongs to the XSD
schema-instance namespace and declares an element’s XSD schema type. Here the type
attribute indicates that the element is of type Car. Also note that all fields
of the Car class are present in the XML output, not only the members of the Vehicle
class.
NOTE: The Deserialize() method will read xsi:type attributes and throw
an exception if it does not find a matching mapping for the type. Without the xsi:type
attribute it would deserialize the XML document to a Vehicle object and ignore
the VIN element.