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.
First posted :
03/24/2008
Times viewed :
357
Serializing an Object Graph
Object serialization itself is simple. The SoapFormatter
class from the System.Runtime.Serialization.Formatters.Soap
namespace provides a Serialize()
method with the two overloads shown in the table. When we invoke the method, it
writes a complete SOAP message with the serialized object inside the body to
the current position of the passed in stream.
Table The SoapFormatter offers two versions of the
Serialize() method. One produces a simple SOAP message with only a body element,
the other allows to us to add headers as well.
Method
Description
public void Serialize( Stream s, object o )
Serializes an object (graph) embedded in the body of a SOAP
message into the specified stream.
public void Serialize( Stream s, object o, Header[] headers
)
Serializes an object (graph) embedded in the body of a SOAP
message into the specified stream. The headers array specifies headers in to
include in the SOAP message.
With the second of the two overloads we can create messages
containing a header section in addition to the body section for the serialized
object. We will see how to use them later.
Both methods can process any object marked with the SerializableAttribute.
Unlike the XmlSerializer
from chapter 9, the SoapFormatter
does not impose any limitations on the objects in can serialize. We can
serialize doubly-linked lists, dictionaries and everything else we can think of
as long as they can furnish the SerializableAttribute. The SoapFormatter exposes properties
(table 13.2) to allow for some customization of the message format and the
behavior of the formatter. There is no need for any properties to customize the
XML format of the serialized objects, because the SOAP standard defines very
clear guidelines for the format of serialized objects inside a message.
Moreover, the goal of runtime serialization is to ensure that a serialized
object is always deserialized to the same type. Ideally the serialization
format is never an issue because nobody other than a SoapFormatter ever looks at the
serialized data. The XmlSerializer
on the other hand does not guarantee nor mandate that the serialized data is
deserialized to the same object type. Its focus is on interoperability and
allows deserialization to a different type as long as the XML format is
compatible with the format of the original class.
The little customization of the serialization format possible
is done through the ISerializable
interface or registered SerializationSurrogates.
Table The SoapFormatter exposes several property to customize
the generated message. These properties apply to the format of the message, not
the format of the serialized object.
Property
Type
Description
AssemblyFormat
FormatterAssemblyStyle
Controls the format of assembly references in the generated
SOAP message. The references are used to locate assemblies when an object is
deserialized.
Binder
SerializationBinder
Customizes how types and assembly references in a SOAP
message map to installed assemblies. We can assign a custom
SerializationBinder for unavailable types or assemblies or changes in
namespaces or type names.
Context
StreamingContext
Contains information about the destination of the
serialized message. It is passed to objects that handle their own
serialization through ISerializable.
See previous chapter for more information.
SurrogateSelector
ISurrogateSelector
Registers substitutes to control the serialization process
for certain types.
See previous chapter for more information.
TopObject
ISoapMessage
Sets the ISoapMessage to receive the data in a deserialized
RPC-style message. See chapter 14 for details.
TypeFormat
FormatterTypeStyle
Controls when object reference identifiers are generated in
the SOAP message.
Now that we know the Serialize() method we can start with
the first part of our example application: A sender class to hide all the
details of serializing objects and sending messages behind an easy-to-use
interface. It is shown in Listing
Listing The SoapMessageSender class send objects encoded
as SOAP messages to message queues.
using System;
using System.IO; // for Stream
using System.Messaging; // for Message and MessageQueue
using System.Runtime.Serialization.Formatters.Soap;
public class SoapObjectSender
{
private MessageQueue
_DestinationQueue;
private SoapFormatter
_Formatter = new SoapFormatter();
public
SoapObjectSender(string destinationQueueName ){
_DestinationQueue =
new MessageQueue(destinationQueueName);
_Formatter = new
SoapFormatter(); |#1
}
public void SendObject(
object o ){
Message msg = new
Message(); |#2
_Formatter.Serialize(
msg.BodyStream, o ); |
_DestinationQueue.Send( msg );
}
}
(annotation) <#1 Instantiate a new SoapFormatter and
keep it around for the lifetime of the sender.>
(annotation) <#2 The Serialize method can serialize
the object directly into the message through the BodyStream property.>
Each SoapObjectSender
object creates a SoapFormatter
that it will use to send all objects. Instantiating a SoapFormatter is not expensive
operation, but keeping one around will save some cycles if we send more than
one message. In the same vein, we designed the SoapObjectSender to send messages
always to the same queue to reduce any overhead associated with opening and
closing a queue.
Once we have instantiated a sender object, we can call SendObject() to
send objects packaged up in SOAP messages. All we have to do is to pass in the
object we want to send, SendObject()
does the rest.
Now that we have a sender, we need something to send. Since
we are building an application to asynchronously perform jobs in a user
management system, we need a user class to encapsulate the user data and a job
class to encode the action to perform when the management application processes
the received message.
Listing The User class stores user data
namespace Christoph.SoapMQ.Shared
{
[Serializable]
public class User {
public User(string
name, int id) {
_Id = id;
_Name = name;
}
private int _Id;
private string _Name;
public int Id { get {
return _Id; } }
public string Name {
get { return _Name; } }
…
}
} // namespace
Because we are trying to keep the example simple our user
class only contains two fields and two read-only properties: One to store the
user’s name and one for a numeric id. This simplistic user class still has an
interesting twist to it. The int type is a value type in the .NET type system, string is a
reference type. Keep this in mind until we look at the serialized object.
The user class does not perform any operations specific to
user management. These operations are implemented in separate job classes. Our
application transmits job objects via message queues to an application that
executes each received object in a batch fashion. Now let’s look at the job
class to insert a new user into the system.
Listing An InsertJob object will insert a new user into
the system when the Run method is executed