Проблемы с сериализацией ToList в словаре - PullRequest
4 голосов
/ 15 февраля 2012

Я попытался сериализовать значения словаря, используя опцию ToList. Я обнаружил, что в процессе десериализации я получил null для всех объектов, которые я сериализовал Это не произошло, когда я использовал поток памяти, и не произошло, когда я использовал объект .Net в качестве типа в словаре. Ниже приведен пример кода, который я создал, который показывает проблему Выход этого кода Словарь: 0-0 Словарь: 1-1 Список: 0 Список: 1 Словарь: 0-ноль Словарь: 1-ноль Список: 0

class Program
{
    static void Main(string[] args)
    {
        A state = new A();

        Stream stream = File.Open("D:\\temp\\temp.txt", FileMode.Create);
        BinaryFormatter bFormatter = new BinaryFormatter();
        bFormatter.Serialize(stream, state);
        stream.Close();

        state.PrintData();

        stream = File.Open("D:\\temp\\temp.txt", FileMode.Open);
        bFormatter = new BinaryFormatter();
        state = (A)bFormatter.Deserialize(stream);
        stream.Close();

        state.PrintData();
    }
}

[Serializable()]
public class A : ISerializable
{
    Dictionary<int, B> dic = new Dictionary<int, B>();
    List<B> list = new List<B>();

    public A()
    {
        for (int i = 0; i < 4; i++)
        {
            dic.Add(i, new B(i));
            list.Add(new B(i));
        }
    }

    public void PrintData()
    {
        foreach (KeyValuePair<int, B> kvp in dic)
        {
            Console.WriteLine("Dictionary: " + kvp.Key.ToString() + "-" + ((kvp.Value != null) ? kvp.Value.ToString() : "Null"));
        }
        foreach(B b in list)
        {
            Console.WriteLine("List: " + b.ToString());
        }
    }

    public A(SerializationInfo info, StreamingContext context)
    {
        List<int> keys = info.GetValue("keys", typeof(List<int>)) as List<int>;
        List<B> values = info.GetValue("values", typeof(List<B>)) as List<B>;

        int count = keys.Count;
        if(count == values.Count)
        {
            for(int i = 0; i < count; i++)
            {
                dic[keys[i]] = values[i];
            }
        }

        list = info.GetValue("list", typeof(List<B>)) as List<B>;
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("keys", dic.Keys.ToList(), typeof(List<int>));
        info.AddValue("values", dic.Values.ToList(), typeof(List<B>));
        List<B> listFromDic = new List<B>(dic.Values.ToList());
        info.AddValue("list", listFromDic, typeof(List<B>));
    }
}

[Serializable()]
public class B : ISerializable
{
    int foo;

    public B(int i)
    {
        foo = i;
    }

    public B(SerializationInfo info, StreamingContext context)
    {
        foo = info.GetInt32("foo");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("foo", foo);
    }

    public override string ToString()
    {
        return (foo != null) ? foo.ToString() : String.Empty;
    }
}

1 Ответ

2 голосов
/ 15 февраля 2012

Словарь поддерживается Serialize. Вот код, который я изменил, который работает.

    public A(SerializationInfo info, StreamingContext context)
    {
        dic = info.GetValue("mapping", typeof(Dictionary<int, B>)) as Dictionary<int, B>;
        list = info.GetValue("list", typeof(List<B>)) as List<B>;
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("mapping", dic, typeof(Dictionary<int, B>));
        List<B> listFromDic = new List<B>(dic.Values.ToList());
        info.AddValue("list", listFromDic, typeof(List<B>));
    }



Edit: упомянутый оп должен использовать список. Пересмотрен код следующим образом. Словарь dic не может быть инициализирован в общедоступном конструкторе A (информация SerializationInfo, контекст StreamingContext).
Потому что:
Порядок десериализации объектов не может быть гарантирован. Например, если один тип ссылается на тип, который еще не был десериализован, произойдет исключение. Если вы создаете типы, имеющие такие зависимости, вы можете обойти эту проблему, реализовав интерфейс IDeserializationCallback и метод OnDeserialization.

выше параграфа происходит от: ISerializable Interface

, что означает, что B не создан в конструкторе A.

Пересмотренный код:

[Serializable()]
public class A : ISerializable, IDeserializationCallback 
{
    Dictionary<int, B> dic = new Dictionary<int, B>();
    List<B> list = new List<B>();
    private List<int> keys = new List<int>();

    public A()
    {
        for (int i = 0; i < 4; i++)
        {
            dic.Add(i, new B(i));
            list.Add(new B(i));
        }
    }

    public void PrintData()
    {
        foreach (KeyValuePair<int, B> kvp in dic)
        {
            Console.WriteLine("Dictionary: " + kvp.Key.ToString() + "-" + ((kvp.Value != null) ? kvp.Value.ToString() : "Null"));
        }
        foreach (B b in list)
        {
            Console.WriteLine("List: " + b.ToString());
        }
    }

    public A(SerializationInfo info, StreamingContext context)
    {
        keys = info.GetValue("keys", typeof(List<int>)) as List<int>;
        List<B> values = info.GetValue("values", typeof(List<B>)) as List<B>;

        list = info.GetValue("list", typeof(List<B>)) as List<B>;
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("keys", dic.Keys.ToList(), typeof(List<int>));
        info.AddValue("values", dic.Values.ToList(), typeof(List<B>));
        List<B> listFromDic = new List<B>(dic.Values.ToList());
        info.AddValue("list", listFromDic, typeof(List<B>));
    }

    public void OnDeserialization(object sender)
    {
        dic = new Dictionary<int, B>();
        int count = keys.Count;
        if (count == list.Count)
        {
            for (int i = 0; i < count; i++)
            {
                dic[keys[i]] = list[i];
            }
        }
    }
}
...