Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Serialization Surrogates

Up to now, I’ve been discussing how to modify a type’s implementation to control how a type serial- izes and deserializes instances of itself. However, the formatters also allow code that is not part of the type’s implementation to override how a type serializes and deserializes its objects. There are two main reasons why application code might want to override a type’s behavior:

■ It allows a developer the ability to serialize a type that was not originally designed to be serialized.

■ It allows a developer to provide a way to map one version of a type to a different version of a type.

Basically, to make this mechanism work, you first define a “surrogate type” that takes over the actions required to serialize and deserialize an existing type. Then, you register an instance of your surrogate type with the formatter telling the formatter which existing type your surrogate type is responsible for acting on. When the formatter detects that it is trying to serialize or deserialize an in- stance of the existing type, it will call methods defined by your surrogate object. Let’s build a sample that demonstrates how all this works.

A serialization surrogate type must implement the System.Runtime.Serialization.ISerial­ izationSurrogate interface, which is defined in the FCL as follows.

 

public interface ISerializationSurrogate {

void GetObjectData(Object obj, SerializationInfo info, StreamingContext context);

 

Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector);

}

 

Now, let’s walk through an example that uses this interface. Let’s say your program contains some DateTime objects that contain values that are local to the user’s computer. What if you want to serial- ize the DateTime objects to a stream but you want the values to be serialized in universal time? This would allow you to send the data over a network stream to another machine in another part of the world and have the DateTime value be correct. Although you can’t modify the DateTime type that ships with the FCL, you can define your own serialization surrogate class that can control how Date­ Time objects are serialized and deserialized. Here is how to define the surrogate class.

 

internal sealed class UniversalToLocalTimeSerializationSurrogate : ISerializationSurrogate { public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) {

// Convert the DateTime from local to UTC

info.AddValue("Date", ((DateTime)obj).ToUniversalTime().ToString("u"));

}

 

public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {

// Convert the DateTime from UTC to local

return DateTime.ParseExact(info.GetString("Date"), "u", null).ToLocalTime();

}

}


The GetObjectData method here works just like the ISerializable interface’s GetObject­ Data method. The only difference is that ISerializationSurrogate’s GetObjectData method takes one additional parameter: a reference to the “real” object that is to be serialized. In the Get­ ObjectData method above, this object is cast to DateTime, the value is converted from local time to universal time, and a string (formatted using universal full date/time pattern) is added to the SerializationInfo collection.



The SetObjectData method is called in order to deserialize a DateTime object. When this method is called, it is passed a reference to a SerializationInfo object. SetObjectData gets the string date out of this collection, parses it as a universal full date/time formatted string, and then converts the resulting DateTime object from universal time to the machine’s local time.

The Object that is passed for SetObjectData’s first parameter is a bit strange. Just before calling SetObjectData, the formatter allocates (via FormatterServices’s static GetUninitialized­ Object method) an instance of the type that the surrogate is a surrogate for. The instance’s fields are all 0/null and no constructor has been called on the object. The code inside SetObjectData can simply initialize the fields of this instance by using the values from the passed-in Serialization­ Info object and then have SetObjectData return null. Alternatively, SetObjectData could create an entirely different object or even a different type of object and return a reference to this new ob- ject, in which case, the formatter will ignore any changes that may or may not have happened to the object it passed in to SetObjectData.

In my example, my UniversalToLocalTimeSerializationSurrogate class acts as a surrogate for the DateTime type, which is a value type. And so, the obj parameter refers to a boxed instance of a DateTime. There is no way to change the fields in most value types (because they are supposed to be immutable) and so, my SetObjectData method ignores the obj parameter and returns a new DateTime object with the desired value in it.

At this point, I’m sure you’re all wondering how the formatter knows to use this ISerialization­ Surrogate type when it tries to serialize/deserialize a DateTime object. The following code demon- strates how to test the UniversalToLocalTimeSerializationSurrogate class.

 

private static void SerializationSurrogateDemo() { using (var stream = new MemoryStream()) {

// 1. Construct the desired formatter IFormatter formatter = new SoapFormatter();

 

// 2. Construct a SurrogateSelector object SurrogateSelector ss = new SurrogateSelector();

 

// 3. Tell the surrogate selector to use our surrogate for DateTime objects ss.AddSurrogate(typeof(DateTime), formatter.Context,

new UniversalToLocalTimeSerializationSurrogate());

 

// NOTE: AddSurrogate can be called multiple times to register multiple surrogates

 

// 4. Tell the formatter to use our surrogate selector formatter.SurrogateSelector = ss;


// Create a DateTime that represents the local time on the machine & serialize it DateTime localTimeBeforeSerialize = DateTime.Now;

formatter.Serialize(stream, localTimeBeforeSerialize);

 

// The stream displays the Universal time as a string to prove it worked stream.Position = 0;

Console.WriteLine(new StreamReader(stream).ReadToEnd());

 

// Deserialize the Universal time string & convert it to a local DateTime stream.Position = 0;

DateTime localTimeAfterDeserialize = (DateTime)formatter.Deserialize(stream);

 

// Prove it worked correctly:

Console.WriteLine("LocalTimeBeforeSerialize ={0}", localTimeBeforeSerialize); Console.WriteLine("LocalTimeAfterDeserialize={0}", localTimeAfterDeserialize);

}

}

 

After steps 1 through 4 have executed, the formatter is ready to use the registered surrogate types. When the formatter’s Serialize method is called, each object’s type is looked up in the set maintained by the SurrogateSelector. If a match is found, then the ISerializationSurrogate object’s GetObjectData method is called to get the information that should be written out to the stream.

When the formatter’s Deserialize method is called, the type of the object about to be deserial- ized is looked up in the formatter’s SurrogateSelector and if a match is found, then the ISeri­ alizationSurrogate object’s SetObjectData method is called to set the fields within the object being deserialized.

Internally, a SurrogateSelector object maintains a private hash table. When AddSurrogate is called, the Type and StreamingContext make up the key and the ISerializationSurrogate object is the key’s value. If a key with the same Type/StreamingContext already exists, then Add­

Surrogate throws an ArgumentException. By including a StreamingContext in the key, you can register one surrogate type object that knows how to serialize/deserialize a DateTime object to a file and register a different surrogate object that knows how to serialize/deserialize a DateTime object to a different process.

       
   
 
 


Surrogate Selector Chains

Multiple SurrogateSelector objects can be chained together. For example, you could have a SurrogateSelector that maintains a set of serialization surrogates that are used for serializing types into proxies that get remoted across the wire or between AppDomains. You could also have a separate SurrogateSelector object that contains a set of serialization surrogates that are used to convert Version 1 types into Version 2 types.

If you have multiple SurrogateSelector objects that you’d like the formatter to use, you must chain them together into a linked list. The SurrogateSelector type implements the ISurrogate­ Selector interface, which defines three methods. All three of these methods are related to chaining. Here is how the ISurrogateSelector interface is defined.

 

public interface ISurrogateSelector {

void ChainSelector(ISurrogateSelector selector); ISurrogateSelector GetNextSelector();

ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector);

}

 

The ChainSelector method inserts an ISurrogateSelector object immediately after the ISurrogateSelector object being operated on (‘this’ object). The GetNextSelector method returns a reference to the next ISurrogateSelector object in the chain or null if the object being operated on is the end of the chain.

The GetSurrogate method looks up a Type/StreamingContext pair in the ISurrogate­ Selector object identified by this. If the pair cannot be found, then the next ISurrogate­ Selector object in the chain is accessed, and so on. If a match is found, then GetSurrogate returns the ISerializationSurrogate object that handles the serialization/deserialization of the type looked up. In addition, GetSurrogate also returns the ISurrogateSelector object that contained the match; this is usually not needed and is ignored. If none of the ISurrogateSelector objects in the chain have a match for the Type/StreamingContext pair, GetSurrogate returns null.

       
   
 
 



Date: 2016-03-03; view: 964


<== previous page | next page ==>
Nbsp;   Serializing a Type As a Different Type and Deserializing an Object As a Different Object | Nbsp;   Overriding the Assembly and/or Type When Deserializing an Object
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.008 sec.)