Json.net:序列化/反序列化不适用于具有循环引用的ISerializable对象

一段时间后我报告了一个问题 ,我在Json.net 4.5 R11中得到了修复。

如果我的循环引用属性Manager为NULL,则序列化和反序列化工作正常。

但是当循环引用属性Manager设置为NON NULL值时,它会在序列化字符串中被忽略,因此它会在反序列化中引发exception。

Json.net问题基础说问题在你的代码中,但我无法弄清楚。 有人可以帮我吗?

问题:

  1. 下面的代码有什么问题吗?
  2. 如果是,我应该怎么做才能解决问题?
  3. 如果没有,那么在Json.net代码中应该做些什么来解决这个问题呢?

一些更新 :在当前使用二进制序列化的遗留应用程序中需要这样做。 由于更改很大,因此使用Json序列化标记标记序列化中涉及的所有私有字段的工作量太大。 由于Json.net可以对ISerializable对象进行序列化,我们希望这样做。 如果没有循环引用对象,则此方法有效。

我的课程

 [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 ) ); //Manager's data not found since json string itself does not have Employee property 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 { public Department Department { get; set; } 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 ); } } 

我的测试代码:

 JsonSerializerSettings jsonSS= new JsonSerializerSettings(); jsonSS.Formatting = Formatting.Indented; jsonSS.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //If there is referenced object then it is not shown in the json serialisation //jsonSS.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; //Throws stackoverflow error jsonSS.PreserveReferencesHandling = PreserveReferencesHandling.All; Department department = new Department(); department.Name = "Dept1"; Employee emp1 = new Employee { Name = "Emp1", Department = department }; department.Manager = null; string json1 = JsonConvert.SerializeObject( emp1, jsonSS ); //json1 = // { // "$id": "1", // "Department": { // "$id": "2", // "Manager": null, // "Name": "Dept1" // }, // "Name": "Emp1" //} Employee empD1 = JsonConvert.DeserializeObject( json1, jsonSS ); //Manager is set as null department.Manager = emp1; //Non null manager is set string json2 = JsonConvert.SerializeObject( emp1, jsonSS ); //SEE Manager property is missing // json2 = { // "$id": "1", // "Department": { // "$id": "2", // "Name": "Dept1" // }, // "Name": "Emp1" //} Employee empD2 = JsonConvert.DeserializeObject( json2, jsonSS ); //Throws exception 

基于khellang的答案,它定位了JSon.Net中的问题,在使用ISerializable接口实现时无法处理循环引用,您可以尝试强制JSon.Net序列化程序忽略ISerializable实现,而不实际删除此实现。
您应该能够通过使用JsonObject属性装饰类( DepartmentEmployee )来实现此目的。
我没有测试这是否真的解决了你的问题。

引用序列化指南 (强调我的):

实现ISerializable的类型被序列化为JSON对象。 序列化时,仅使用从ISerializable.GetObjectData返回的值; 该类型的成员将被忽略。 反序列化时,调用带有Seri​​alizationInfo和StreamingContext的构造函数,传递JSON对象的值。

在不需要此行为的情况下,可以将JsonObjectAttribute放置在实现ISerializable的.NET类型上,以强制将其序列化为普通的JSON对象。

由于您的问题和评论可以追溯到2012年,因此可能无法使用此解决方案。 即使使用ISerializable ,当前的JSon.Net实现也可以处理循环引用。

我会保持简短:)

  1. 它似乎是ISerializable接口ISerializable了它。 如果你删除它,一切都很完美。

  2. 您是否有充分的理由在EmployeeDepartment实现ISerializable接口? 如果没有,只需将其删除即可。 如果你这样做,GOTO 3;)

  3. 我不知道Newtonsoft.Json内部,但不知何故,它在实现ISerializable时没有正确处理循环引用。 您应该在GitHub上提出问题。

但是当循环引用属性管理器设置为NON NULL值时,它会在序列化字符串中被忽略,因此它会在反序列化中引发exception。

因为循环引用被忽略。 这就是ReferenceLoopHandling.Ignore的要点。

PreserveReferencesHandling不适用于ISerializable,因为必须在父值之前创建子值。

看起来你真的不需要实现Iserializable接口。 这种类结构很简单。 我已经序列化了非常大的类而不必走那么远。

如果您仍有循环引用问题,请尝试以下操作:

  1. 我相信JSON.net有一个格式化选项来忽略循环引用

    JsonSerializerSettings jsSettings = new JsonSerializerSettings(); jsSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

  2. 忽略违规财产

  3. 重组你的课程