Исключение выдается при десериализации объектов самоссылки при использовании ISerializationSurrogate - PullRequest
1 голос
/ 07 апреля 2020

Исключение выдается, когда я пытаюсь десериализовать объект самоссылки.

System.Runtime.Serialization.SerializationException: "Объект с идентификатором 1 указан в информации об адресе ссылки, но объект не существует. "

Это мой код:

class MySerializationSurrogate : ISerializationSurrogate {
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
        Console.WriteLine("MySerializationSurrogate.GetObjectData()");
    }
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
        Console.WriteLine("MySerializationSurrogate.SetObjectData()");
        var it = info.GetEnumerator();
        while (it.MoveNext()) {
            Console.WriteLine($"{it.ObjectType} {it.Name} {it.Value}");
        }
        return obj;
    }
}

[Serializable]
class Test {
    int prop { get; set; } = 123321;
    Test me { get; set; }
    public Test() { me = this; }
}

class Program {

    static void Save() {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream fs = new FileStream("E:\\a.txt", FileMode.Create);
        Test ch = new Test();
        bf.Serialize(fs, ch);
        fs.Close();
    }

    static void Read() {
        BinaryFormatter bf = new BinaryFormatter();
        SurrogateSelector mss = new SurrogateSelector();
        mss.AddSurrogate(typeof(Test), bf.Context, new MySerializationSurrogate());
        bf.SurrogateSelector = mss;
        FileStream fs = new FileStream("E:\\a.txt", FileMode.Open);
        object ch = bf.Deserialize(fs);
        fs.Close();
    }

    static void Main(string[] args) {
        Save();
        Read();
        Console.ReadLine();
    }
}

В моей консоли не было ничего, поэтому я думаю SetObjectData() и GetObjectData() не был вызван.

Если я удалю mss.AddSurrogate(typeof(Test), bf.Context, new MySerializationSurrogate());, код будет успешно выполнен.

Если я удалю Test.me, код будет успешно выполнен.

Я попытался выяснить, в чем дело, поэтому я создал круговую ссылку, например:

ap = b; bp = a; Serialize (fs, a);

и затем десериализовать, без каких-либо исключений. Таким образом, поддерживается циклическая ссылка.

Похоже, что когда вы используете пользовательский суррогат и пытаетесь десериализовать объект, который имеет сам указатель, будет выдано исключение.

Даже ваш суррогат не был вызван.

Так что не так?

1 Ответ

1 голос
/ 07 апреля 2020

Единственный способ добиться этой работы - использовать сторож для ручного кодирования самоссылочных полезных нагрузок:

    const string Self = "SELF";
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var test = (Test)obj;
        info.AddValue("prop", test.prop);
        if (test.me is null) { }
        else if (ReferenceEquals(test.me, test))
        {
            info.AddValue("me", Self);
        }
        else
        {

            info.AddValue("me", test.me);
        }


    }
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        var it = info.GetEnumerator();
        var test = (Test)obj;
        while (it.MoveNext())
        {
            switch (it.Name)
            {
                case "prop":
                    test.prop = (int)it.Value;
                    break;
                case "me":
                    switch(it.Value)
                    {
                        case string s when s == Self:
                            test.me = test;
                            break;
                        case Test t:
                            test.me = t;
                            break;
                    }
                    break;
            }
        }
        return obj;
    }
...