How to pass custom data to deserialization function
I'm doing some C# IO work and I want to import/export data from a few classes. After looking for a while, it seems serialization is pretty close to what I want.
However, there is a problem. I have a XML file that describes members of a certain class(which I will refer as ValueWithId
), which are aggregated in a class which I will call CollectionOfValuesWithId
for the purposes of this question.
ValueWithId
is a class that contains a string
member called ShortName
, which is unique. There is only one ShortName
per ValueWithId
and all ValueWithId
have a non-null ShortName
. CollectionOfValuesWithId
contains a function to find the ValueWithId
with a given ShortName
.
When serializing, I do NOT want to store ValueWithId
nor Collecti开发者_Go百科onOfValuesWithId
in the output file. Instead, I just want to store the ShortName
in the file.
So far, so good. I just need to use SerializationInfo.AddValue("ValueWithId", MyValueWIthId.ShortName)
.
The problem comes with deserialization. Some googling suggests that to read data from a file one would do this:
public SomeClassThatUsesValueWithId(SerializationInfo info, StreamingContext ctxt)
{
string name = (string)info.GetValue("ValueWithId", typeof(string));
}
However, the string is not enough to recover the ValueWithId
instance. I also need the CollectionOfValuesWithId
. I want something like this:
public SomeClassThatUsesValueWithId(SerializationInfo info,
StreamingContext ctxt, CollectionOfValuesWithId extraParameter)
In other words, I need to pass extra data to the deserialization constructor. Does anyone know any way to do this or any alternatives?
I figured it out. The important class to do this is StreamingContext
.
This class conveniently has a property named Context
(which can be set in the constructor parameter additional
.
From MSDN:
additional
Type: System.Object
Any additional information to be associated with the StreamingContext. This information is available to any object that implements ISerializable or any serialization surrogate. Most users do not need to set this parameter.
So here is some sample code regarding how to do this(tested on Mono, but I think it should work on Windows):
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class ValueWithId
{
public string ShortName;
public string ActualValue;
public ValueWithId(string shortName, string actualValue)
{
ShortName = shortName;
ActualValue = actualValue;
}
public override string ToString()
{
return ShortName + "->" + ActualValue;
}
}
public class CollectionOfValuesWithId
{
private IList<ValueWithId> Values = new List<ValueWithId>();
public void AddValue(ValueWithId val)
{
Values.Add(val);
}
public ValueWithId GetValueFromId(string id)
{
foreach (var value in Values)
if (value.ShortName == id)
return value;
return null;
}
}
[Serializable]
public class SomeClassThatUsesValueWithId : ISerializable
{
public ValueWithId Val;
public SomeClassThatUsesValueWithId(ValueWithId val)
{
Val = val;
}
public SomeClassThatUsesValueWithId(SerializationInfo info, StreamingContext ctxt)
{
string valId = (string)info.GetString("Val");
CollectionOfValuesWithId col = ctxt.Context as CollectionOfValuesWithId;
if (col != null)
Val = col.GetValueFromId(valId);
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
//Store Val.ShortName instead of Val because we don't want to store the entire object
info.AddValue("Val", Val.ShortName);
}
public override string ToString()
{
return "Content="+Val;
}
}
class MainClass
{
public static void Main(string[] args)
{
CollectionOfValuesWithId col = new CollectionOfValuesWithId();
col.AddValue(new ValueWithId("foo", "bar"));
SomeClassThatUsesValueWithId sc = new SomeClassThatUsesValueWithId(col.GetValueFromId("foo"));
BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File, col));
using (var stream = new FileStream("foo", FileMode.Create))
{
bf.Serialize(stream, sc);
}
col.GetValueFromId("foo").ActualValue = "new value";
using (var stream2 = new FileStream("foo", FileMode.Open))
{
Console.WriteLine(bf.Deserialize(stream2));
}
}
}
The output I get is:
Content=foo->new value
Which is exactly what I wanted.
精彩评论