开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜