|
XML Serialization
XML serialization differs from binary serialization in two ways.
The following list describes the item types that can be serialized using XML serialization:
Before getting into too much detail, let’s take a quick look at a simple XML serialization sample. The following code fragment is the class definition to be serialized:
C#
public class MyXmlSimpleData { public int IntField; public string StringField; public DateTime CurrentDate; private int PrivateField;
public MyXmlSimpleData() { IntField = 1234; StringField = "Xml Simple Serialization"; CurrentDate = DateTime.Today; PrivateField = 333; } }
|
Visual Basic .NET
Public Class MyXmlSimpleData
Public IntField As Integer
Public StringField As String
Public CurrentDate As DateTime
Private PrivateField As Integer
Public Sub New()
IntField = 1234
StringField = "Xml Simple Serialization"
CurrentDate = DateTime.Today
PrivateField = 333
End Sub
End Class
First notice that no special attribute tags are required to indicate that this class can be serialized, as is the case with binary serialization. The process of serializing the class is extremely simple, as the following code illustrates:
C#
MyXmlSimpleData xmlData;
XmlSerializer xmlDataSerializer;
StreamWriter streamFileWriter;
xmlData = new MyXmlSimpleData();
xmlDataSerializer = new XmlSerializer( typeof( MyXmlSimpleData ) );
streamFileWriter = new StreamWriter( "simple.xml" );
xmlDataSerializer.Serialize( streamFileWriter, xmlData );
Visual Basic .NET
Dim xmlData As MyXmlSimpleData
Dim xmlDataSerializer As XmlSerializer
Dim streamFileWriter As StreamWriter
xmlData = new MyXmlSimpleData()
xmlDataSerializer = new XmlSerializer( GetType( MyXmlSimpleData ) )
streamFileWriter = new StreamWriter( "simple.xml" )
xmlDataSerializer.Serialize( streamFileWriter, xmlData )
In this example, an instance of the MyXmlSimpleData class is instantiated first and then XmlSerializer is created, which requires the data type of the class to be serialized, this case, MyXmlSimpleData. In the next step, the stream is created, which is a simple file. Finally, the object is serialized to the stream. Once the object is serialized to the stream, the following code will deserialize it:
C#
MyXmlSimpleData xmlData;
XmlSerializer xmlDataSerializer;
FileStream xmlFileStream;
xmlDataSerializer = new XmlSerializer( typeof( MyXmlSimpleData ) );
xmlFileStream = new FileStream( "simple.xml", FileMode.Open );
xmlData = (MyXmlSimpleData) xmlDataSerializer.Deserialize( xmlFileStream );
Visual Basic .NET
Dim xmlData As MyXmlSimpleData
Dim xmlDataSerializer As XmlSerializer
Dim xmlFileStream As FileStream
xmlDataSerializer = new XmlSerializer( GetType( MyXmlSimpleData ) )
xmlFileStream = new FileStream( "simple.xml", FileMode.Open )
xmlData = xmlDataSerializer.Deserialize( xmlFileStream )
The steps are very similar to the serialization process shown earlier. The difference is that we don’t instantiate an instance of MyXmlSimpleData because we’re retrieving it from the stream and the Deserialize() method is called instead with the data stream.
After serializing the object to the stream, which in this case is a file, you can take a look at the resulting XML. The following code is the XML generated for the preceding class. Notice that only the public properties are included, that is, there’s no PrivateField entry. Also notice that there’s no type information, just the values for each field.
<?xml version="1.0" encoding="utf-8" ?> <MyXmlSimpleData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <IntField>1234</IntField> <StringField>Xml Serialization Sample</StringField> <CurrentDate>2003-02-09T00:00:00.0000000-08:00</CurrentDate> </MyXmlSimpleData> |
As is the case with binary serialization, XML serialization involves attributes that control how a class is serialized using the XmlSerializer formatter. One such attribute is the XmlElement attribute. Each attribute contains any number of constructors as well as properties that can be assigned in the attribute applied to a class or a class member. For example, in the following code snippet, the XmlElement attribute is used to rename the IntField1 property from the default value of IntField1 to Integer_Field:
C#
public class MyRenamedXmlData
{
[ XmlElement( ElementName = "Integer_Field" ) ]
public int IntField1;
}
Visual Basic .NET
Public Class MyRenamedXmlData <XmlElement("Integer_Field")> _ Public IntField1 As Integer End Class
As mentioned, the XmlElement attribute is just one of many attributes that can be applied to a class definition that affects the generated XML. Table 4-2 lists the different attributes that can be applied to classes and class members. Notice that when specifying an attribute within a class definition, the last Attribute text is left off. For example, in the preceding code snippet, we specified XmlElement instead of XmlElementAttribute. Also, within the parentheses following each XML attribute, any public properties exposed by that attribute class can be set. In this example, ElementName is a property of the XmlElementAttribute class. The Visual Basic .NET syntax is slightly different. First, instead of square brackets, angled brackets denote the attribute, and second, the property is passed to the constructor for the XmlElementAttribute class. |
|
Table 4-2: XML Attributes
|
|
Attribute |
Description |
XmlAnyAttributeAttribute |
Indicates that all XML attributes unknown to the schema should be placed in an array of XmlAttribute objects on deserialization. |
XmlAnyElementAtribute |
Indicates that all XML elements unknown to the schema should be placed in an array of XmlElement objects on deserialization. |
XmlArrayAttribute |
Controls properties of an array. |
XmlArrayItemAttribute |
Controls individual elements within an array. |
XmlAttributeAttribute |
Indicates that the class should be serialized as an XML attribute. |
XmlChoiceIdentifierAttribute |
Indicates that the member can be described by an enumerated list. |
XmlElementAttribute |
Indicates that the member will be serialized as an XML element. |
XmlEnumAttribute |
Controls the element name of an enumeration member. |
XmlIgnoreAttribute |
Indicates that the member should be ignored when serialized. |
XmlIncludeAttribute |
Indicates that the class should be included when generating schemas. |
XmlNamespaceDeclarationsAttribute |
Indicates that a property, parameter, return value, or class member contains a prefix associated with namespaces used within the XML document. |
XmlRootAttribute |
Indicates a class, a structure, an enumeration, or an interface as the root element of the XML document |
XmlTextAttribute |
Indicates that the member should be serialized as XML text. |
XmlTypeAttribute |
Controls the name and namespace of the XML type. |
Although we won’t get into the specifics of every attribute that can possibly be applied to a class, the program examples presented at the end of this section illustrate several XML attributes. For a full description of each attribute and its properties, consult the Platform SDK or the .NET Framework SDK.
As we saw in the previous section, the XML generated in the serialization process can be modified by applying attributes. However, what if you have a situation in which you need to generate multiple XML streams? Let’s say, for instance, that in the MyRenamedXmlData class, the property name for IntField1 needed to be different depending on the consumer of the serialized XML. In this case, applying an attribute would not suffice because the applied attribute is always used when using the default XML serializer. You need to override the default XML serialization. The process of overriding the default XML serialization requires the following steps:
The following code illustrates overriding the default XML serialization process by implementing the custom serialization in the OverrideSerialization method:
C#
public class MyXmlOverrideSample
{
public int IntField1;
public DateTime CurrentDate;
public void OverrideSerialization(FileStream fileStream)
{
XmlElementAttribute xmlElementAttribute = new XmlElementAttribute();
XmlAttributes xmlAttributes = new XmlAttributes();
XmlAttributeOverrides xmlAttributeOverrides = new XmlAttributeOverrides();
XmlSerializer xmlSerializer = null;
xmlElementAttribute.ElementName = "Override_Integer_Field_Numero_Uno";
xmlAttributes.XmlElements.Add(xmlElementAttribute);
xmlAttributeOverrides.Add(typeof(MyXmlOverrideSample), "IntField1", xmlAttributes);
xmlSerializer = new XmlSerializer(typeof(MyXmlOverrideSample), xmlAttributesOverride);
xmlSerializer.Serialize(filesStream, this);
}
}
Visual Basic .NET
Public Class MyXmlOverrideSample
Public IntField1 As Integer
Public CurrentDate As DateTime
Public Sub OverrideSerialization(ByVal fileDataStream As Filestream)
Dim xmlElementAttr As XmlElementAttribute = New XmlElementAttribute()
Dim xmlAttr As XmlAttributes = New XmlAttributes()
Dim xmlOverrides As XmlAttributeOverrides = New _
XmlAttributeOverrides()
Dim xmlDataSerializer As XmlSerializer
xmlElementAttr.ElementName = "Override_Integer_Field_Numbero_Uno"
xmlAttr.XmlElements.Add(xmlElementAttr)
xmlOverrides.Add(GetType(MyXmlOverrideSample), "IntField1", _
xmlAttr)
xmlDataSerializer = New XmlSerializer(GetType(MyXmlOverrideSample), _
xmlOverrides)
xmlDataSerializer.Serializer(fileDataStream, this)
End Sub
End Class
This snippet example shows a method that serializes the class to a stream but changes the XML element name for the IntField1 member to Override_Integer_Field_Numero_Uno. Note that when the object is serialized, the remaining properties that were not overridden are serialized as expected.
This class represents a collection of attribute objects that control how the XmlSerializer serializes and deserializes an object. Creating the XmlAttributes is part of a process that overrides the default way the XmlSerializer serializes class instances. For example, suppose you want to serialize an object that is created from a DLL which has an inaccessible source. By using the XmlAttributeOverrides, you can augment or otherwise control how the object is serialized.
The members of the XmlAttributes class correspond directly to a family of attribute classes that control serialization. For example, the XmlText property must be set to an XmlTextAttribute, which allows you to override serialization of a field or property by instructing the XmlSerializer to serialize the property value as XML text. For a complete list of attributes that control serialization, see the XmlSerializer.
This class allows you to override property, field, and class attributes when you use the XmlSerializer to serialize or de-serialize an object.
The XmlAttributeOverrides enables the XmlSerializer to override the default way of serializing a set of objects. Overriding serialization in this way has two uses: first, you can control and augment the serialization of objects found in a DLL, even if you do not have access to the source; second, you can create one set of serializable classes, but serialize the objects in multiple ways. For example, instead of serializing members of a class instance as XML elements, you can serialize them as XML attributes, resulting in a more efficient document to transport.
After you create an XmlAttributeOverrides object, you pass it as an argument to the XmlSerializer constructor. The resulting XmlSerializer uses the data contained by the XmlAttributeOverrides to override attributes that control how objects are serialized. To accomplish this, the XmlAttributeOverrides contains a collection of the object types that are overridden, as well as an XmlAttributes object associated with each overridden object type. The XmlAttributes object itself contains an appropriate set of attribute objects that control how each field, property, or class is serialized. The process for creating and using an XmlAttributeOverrides object is as follows:
1. Create an XmlAttributes object.
2. Create an attribute object that is appropriate to the object being overridden. For example, to override a field or property, create an XmlElementAttribute, using the new, derived type. You can optionally assign a new ElementName, or Namespace that overrides the base class's attribute name or namespace.
3. Add the attribute object to the appropriate XmlAttributes property or collection. For example, you would add the XmlElementAttribute to the XmlElements collection of the XmlAttributes object, specifying the member name that is being overridden.
4. Create an XmlAttributeOverrides object.
5. Using the Add() method, add the XmlAttributes object to the XmlAttributeOverrides object. If the object being overridden is an XmlRootAttribute or XmlTypeAttribute, you need only to specify the type of the overridden object. But if you are overriding a field or property, you must also specify the name of the overridden member.
6. When constructing the XmlSerializer, pass the XmlAttributeOverrides to the XmlSerializer constructor.
7. Use the resulting XmlSerializer to serialize or de-serialize the derived class objects.
The following example serializes an instance of a class named Orchestra, which contains a single field named Instruments that returns an array of Instrument objects. A second class named Brass inherits from the Instrument class. The example creates an XmlAttributes object to override the Instrument field, allowing the field to accept Brass objects and adds the XmlAttributes object to an instance of the XmlAttributeOverrides class.