< VB .NET Binary Client-Server Socket Project Example 2 | Main | C++ & C# XML Serialization Program Examples >

 


 

Chapter 4 Part 10:

Serialization

 

 

What do we have in this chapter 4 Part 10?

  1. XML Serialization

  2. Controlling XML Serialization

  3. Overriding XML Serialization

  4. XmlAttributes Class

  5. XmlAttributeOverrides Class

 

 

XML Serialization

 

XML serialization differs from binary serialization in two ways.

 

  1. The types of data that can be serialized are limited
  2. Type information is not serialized with the data, which means that there’s no guarantee that the serialized object will be deserialized to the same object type.

 

The following list describes the item types that can be serialized using XML serialization:

 

  1. Public fields and read/write properties of public classes (read-only properties will not be serialized)
  2. Classes implementing ICollection and IEnumerable
  3. XmlElement objects
  4. XmlNode objects
  5. DataSet objects
  6. Classes with default constructors

 

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>

 

Controlling XML Serialization

 

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.

 

Overriding XML Serialization

 

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:

 

  1. Create one or more XML attributes (such as those listed in Table 4-2) that apply to one element within the class to serialize. Remember that these attributes are actually classes with properties and methods.
  2. Create an instance of the XmlAttributes class that each XML attribute created in step 1 is added to.
  3. Create an instance of the XmlAttributeOverrides class that applies the XmlAttributes class to a specified class property or method.

 

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.

 

XmlAttributes Class

 

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.

 

XmlAttributeOverrides Class

 

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.

 

 

 


 

< VB .NET Binary Client-Server Socket Project Example 2 | Main | C++ & C# XML Serialization Program Examples >