Exception threw out when I try to deserialize a self-reference object.
System.Runtime.Serialization.SerializationException: "The object with ID 1 is referenced in the link address information, but the object does not exist."
This is my code:
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();
}
}
There wasn't any out put in my console, so I think
SetObjectData()
andGetObjectData()
hasn't been called.If I delete
mss.AddSurrogate(typeof(Test), bf.Context, new MySerializationSurrogate());
the code will run successfully.If I remove
Test.me
the code will run successfully.I tried to find out what's wrong, so I created a circular reference like this:
a.p=b;b.p=a;Serialize(fs,a);
and then deserialize, no any exceptions. So circular reference is supported.
It seems that when you use a user-defined surrogate and try to deserialize an object that has a self-pointer, the exception will be thrown.
Even your surrogate wasn't called.
So what's wrong?
The only way I can get that working is to use a sentinel for manually encoding self-referential payloads:
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;
}