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.
Previously we covered developing classes containing metadata
attributes that the XmlSerializer maps to an XML type. This is great when we
develop classes ourselves, but we can not apply this technique when we are in
not in control of the source code. But does that mean we are out of luck when
we need to customize how classes from third-party libraries map to XML or when
the mappings defined by a class need modification?
No, it does not. Fortunately there are alternatives to attaching
attributes in code. There are several overloads for the constructor of the
XmlSerializer to customize type mappings at runtime. Table 10.1 shows the
available overloads. These constructors can customize type mappings and declare
additional types to the XmlSerializer.
1.3
Table 10.1: All available
overloads for the constructor of the XmlSerializer class. Each overload allows
customizing the serialization at run-time
Constructor
Signature
Description
XmlSerializer(Type
type);
Create
an XmlSerializer for type
XmlSerializer(XmlTypeMapping
mapping);
Provide a custom
type mapping to use for serialization
XmlSerializer(Type
type,
string defaultNamespace);
Create an
XmlSerializer for type and
specify the default namespace for all serialized objects
XmlSerializer(Type
type, Type[] extraTypes);
Create an
XmlSerializer for type and the
types in the extraType
array
XmlSerializer(Type
types,
XmlAttributeOverrides overrides);
Create an
XmlSerializer for type and apply
customization the attributes in overrides
XmlSerializer(Type
type, XmlRootAttribute root);
Create an XmlSerializer
for type and change the root element to root
Create an
XmlSerializer for type and the
types in the extraType
array, apply customization the attributes in overrides, change the root element to root and specify the default namespace for all
serialized objects
Previously we learned that the XmlSerializer has to analyze the
types it is going to process before it is ready to convert XML to objects and
vice-versa. We also learned several techniques based on metadata attributes to
explicitly declare derived types, which the serializer can not automatically
discover during the analysis. During the class analysis, the serializer checks
for the attributes and records the type substitutions they declare. Obviously
theses techniques only work to declare substitutions with types that exist at
the time we write the declarations. We need a different solution for
substitutions we can not declare in code. Take a collection class, like the
ArrayList. There is no way Microsoft could declare all the types people are
going to store inside an ArrayList. In fact they did not attach attributes
inside the ArrayList class. Hence, serializing an ArrayList referencing
anything else than objects of type object like in the following example causes
an exception.
Serialize() throws the exception because the XmlSerializer’s
initial type analysis for the ArrayList found that all elements inside the
ArrayList are declared to be of type object because the ArrayList needs to be
able to reference ANY type. If we want to serialize an ArrayList referencing
anything other than objects we need to declare the types differently.
The XmlSerializer allows declaring type global globally, by
passing a Type array with the global types to the constructor. The following
line, for example, would declare the string type within the ArrayList to avoid
the exception in the example above:
XmlSerializer serializer
= new
XmlSerializer(typeof(ArrayList),
// primary type declaration
new Type[]
{typeof(string)} ); // global type
declaration.
With the global type declaration in place, the ArrayList in the
example above serializes to the following XML document.
The root element’s name follows the XmlSerializer’s naming scheme
for all arrays and collections: It appends the type of the items within the
array to “ArrayOf”. The array item’s node name identifies the declared item
type (“anyType” is the XSD equivalent of the Object class, i.e. the root class
of all classes). To identify the type of the actual object, the XmlSerializer
added an xsi:type="xsd:string" attribute to the element.
For every field of an object the XmlSerializer serializes, it
first checks the referenced object’s type against the declared type of the
field. If the object’s type does not match the declared type, it checks against
types declared by an XmlIncludeAttribute attached to the declared type.
Finally, if the object’s type does not match any included type, it checks the
global type declarations. If the serializer finds a match it proceeds to serialize
the type. Otherwise it will throw an exception.
When the XmlSerializer deserializes an XML type it checks whether
an element matches the corresponding field name. If the name matches, it checks
the element for an xsi:type attribute and compares the attribute’s values
against the global type declarations. When it finds a match it deserializes the
object in the stream based on the type mapping for the matching type.