【问题标题】:StackOverflowException is thrown when serialising circular dependent ISerializable object with ReferenceLoopHandling.Ignore使用 ReferenceLoopHandling.Ignore 序列化循环依赖 ISerializable 对象时引发 StackOverflowException
【发布时间】:2012-11-04 22:17:19
【问题描述】:

我有一个使用二进制序列化来持久化数据的遗留应用程序。现在我们想使用 Json.net 4.5 来序列化数据,而无需对现有类进行太多更改。

在我们遇到循环依赖类之前,一切都很好。有什么办法可以解决这个问题吗?

示例代码如下所示

[Serializable]
class Department : ISerializable
{
    public Employee Manager { get; set; }
    public string Name { get; set; }

    public Department() { }
    public Department( SerializationInfo info, StreamingContext context )
    {
        Manager = ( Employee )info.GetValue( "Manager", typeof( Employee ) );
        Name = ( string )info.GetValue( "Name", typeof( string ) );
    }
    public void GetObjectData( SerializationInfo info, StreamingContext context )
    {
        info.AddValue( "Manager", Manager );
        info.AddValue( "Name", Name );
    }
}

[Serializable]
class Employee : ISerializable
{
    [NonSerialized] //This does not work
    [XmlIgnore]//This does not work
    private Department mDepartment;
    public Department Department
    {
        get { return mDepartment; }
        set { mDepartment = value; }
    }

    public string Name { get; set; }

    public Employee() { }
    public Employee( SerializationInfo info, StreamingContext context )
    {
        Department = ( Department )info.GetValue( "Department", typeof( Department ) );
        Name = ( string )info.GetValue( "Name", typeof( string ) );
    }

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

以及测试代码

Department department = new Department();
department.Name = "Dept1";

Employee emp1 = new Employee { Name = "Emp1", Department = department };
department.Manager = emp1;

Employee emp2 = new Employee() { Name = "Emp2", Department = department };
IList<Employee> employees = new List<Employee>();
employees.Add( emp1 );
employees.Add( emp2 );

var memoryStream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize( memoryStream, employees );

memoryStream.Seek( 0, SeekOrigin.Begin );
IList<Employee> deserialisedEmployees = formatter.Deserialize( memoryStream ) as IList<Employee>; //Works nicely

JsonSerializerSettings jsonSS= new JsonSerializerSettings();
jsonSS.TypeNameHandling = TypeNameHandling.Objects;
jsonSS.TypeNameAssemblyFormat = FormatterAssemblyStyle.Full;
jsonSS.Formatting = Formatting.Indented;
jsonSS.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //This is not working!!
//jsonSS.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; //This is also not working!!
jsonSS.PreserveReferencesHandling = PreserveReferencesHandling.All;
string jsonAll = JsonConvert.SerializeObject( employees, jsonSS ); //Throws stackoverflow exception

Edit1:该问题已报告给 Json (http://json.codeplex.com/workitem/23668)

Edit2:序列化在 4.5 R11 版本中可以正常工作,但反序列化仍然无法正常工作

Edit3:实际上,当循环引用对象不为空时,序列化本身不起作用

Edit4:来自 Json.net 问题库的评论是问题出在您的最后并关闭了问题。但我无法找出我的代码有什么问题。我为此发布了另一个 question。谢谢大家的回答,投票...

【问题讨论】:

  • json.net 甚至 ISerializable 吗?
  • 是的,先生,如果没有循环依赖,它工作得非常好。我在 GetObjectData 中设置的所有属性和私有变量也已正确序列化

标签: c# .net json serialization json.net


【解决方案1】:

我认为您需要ReferenceLoopHandling.SerializePreserveReferencesHandling.All 来复制二进制序列化的行为。不过,生成的 JSON 可能没有那么漂亮。

编辑:我深入研究了 JSON.Net 4.5r10 并发现了一个缺陷:JsonSerializerInternalWriter 不会检查 #ShouldWriteReference 以获取通过 ISerializable 获得的引用。

#SerializeISerializable 中的foreach 循环改写如下,您的对象图往返成功。

  foreach (SerializationEntry serializationEntry in serializationInfo)
  {
    writer.WritePropertyName(serializationEntry.Name);
    var entryValue = serializationEntry.Value;
    var valueContract = GetContractSafe(entryValue);
    if (ShouldWriteReference(entryValue, null, valueContract, null, member))
    {
      WriteReference(writer, entryValue);
    }
    else
    {
      SerializeValue(writer, entryValue, valueContract, null, null, member);
    }
  }

【讨论】:

  • 你说的我都试过了,还是抛出异常。
  • 它有效。那就是不抛出异常。但是我没有得到部门对象中的经理。即反序列化时为空
  • 使用 ISerialisable(GetObjectData 和序列化构造函数)的 JSON 序列化和不产生相同的输出。所以看来问题出在反序列化上。任何帮助表示赞赏。
  • @renep 我检查了编写器生成的 JSON,它看起来是正确的,因此读取和写入实现都可能存在错误。我猜ISerializable 处理在 JSON.Net 中确实存在缺陷,仅仅是因为它不常用,或者至少不能替代二进制序列化。
  • 谢谢杰夫。我接受您的回答,并尝试将此错误发布到 Json
【解决方案2】:

你试过了吗?

[Serializable]
 class Employee : ISerializable
 {
   [NonSerialized]
   [XmlIgnore]
   public Department Department { get; set; }

NonSerialized 表示一个可序列化类的字段应该 不被序列化。

XmlIgnore 指示 XmlSerializer 的 Serialize 方法不要 序列化公共字段或公共读写属性值

【讨论】:

  • 我猜 '[NonSerialized]' 只适用于字段。我已将该属性转换为具有支持字段并应用了该属性,但它仍然无法正常工作。更新了问题。但我不想忽略它的价值。无论如何我都想要这个值,这就是它在 GetObjectData 方法中的原因
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
相关资源
最近更新 更多