Как передать пользовательские данные в функцию десериализации - PullRequest
4 голосов
/ 05 октября 2011

Я выполняю некоторую работу по вводу-выводу в C # и хочу импортировать / экспортировать данные из нескольких классов.Через некоторое время кажется, что сериализация довольно близка к тому, что я хочу.

Однако есть проблема.У меня есть XML-файл, который описывает членов определенного класса (который я буду называть ValueWithId), которые объединены в класс, который я буду называть CollectionOfValuesWithId для целей этого вопроса.

ValueWithId - это класс, который содержит string член с именем ShortName, который является уникальным.Существует только один ShortName на ValueWithId, и все ValueWithId имеют ненулевое значение ShortName.CollectionOfValuesWithId содержит функцию для поиска ValueWithId с заданным ShortName.

При сериализации я NOT хочу сохранить ValueWithId или CollectionOfValuesWithId в выходных данныхфайл.Вместо этого я просто хочу сохранить ShortName в файле.Все идет нормально.Мне просто нужно использовать SerializationInfo.AddValue("ValueWithId", MyValueWIthId.ShortName).

Проблема заключается в десериализации.Некоторые специалисты по поиску в Google предполагают, что для чтения данных из файла можно сделать следующее:

public SomeClassThatUsesValueWithId(SerializationInfo info, StreamingContext ctxt)
{
    string name = (string)info.GetValue("ValueWithId", typeof(string));
}

Однако строки недостаточно для восстановления экземпляра ValueWithId.Мне также нужно CollectionOfValuesWithId.Я хочу что-то вроде этого:

public SomeClassThatUsesValueWithId(SerializationInfo info,
   StreamingContext ctxt, CollectionOfValuesWithId extraParameter)

Другими словами, мне нужно передать дополнительные данные в конструктор десериализации.Кто-нибудь знает какой-либо способ сделать это или какие-либо альтернативы?

1 Ответ

6 голосов
/ 06 октября 2011

Я понял это. Важный класс для этого - StreamingContext. Этот класс удобно имеет свойство с именем Context (которое можно установить в параметре конструктора additional.

С MSDN :

дополнительный
Тип: System.Object
Любая дополнительная информация, которая будет связана с StreamingContext. Эта информация доступна любому объекту, который реализует ISerializable или любой суррогат сериализации. Большинство пользователей делают не нужно устанавливать этот параметр.

Итак, вот пример кода относительно того, как это сделать (протестировано на Mono, но я думаю, что оно должно работать на 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));
        }
    }
}

Вывод, который я получаю:

Content=foo->new value

Именно это я и хотел.

...