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

时间:2012-12-10 08:57:33

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

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

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

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

Json.net问题库说问题出在您的代码中,但我无法弄明白。有人可以帮助我吗?

问题:

  1. 下面的代码有问题吗?
  2. 如果是,我应该怎么做才能解决问题?
  3. 如果没有那么应该在Json.net代码中做些什么来解决这个问题?
  4. 更多更新: 在当前使用二进制序列化的遗留应用程序中需要这样做。由于更改很大,因此使用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<Employee>( 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<Employee>( json2, jsonSS );  //Throws exception
    

4 个答案:

答案 0 :(得分:3)

基于khellang的答案,它解决了JSon.Net中使用ISerializable接口实现时无法处理循环引用的问题,您可以尝试强制忽略JSon.Net序列化程序ISerializable实现,但没有实际删除此实现。
您应该可以通过使用Department属性修改课程(EmployeeJsonObject)来实现这一目标。
我没有测试这是否真的能解决你的问题。

引用Serialization Guide(强调我的):

  

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

     

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

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

答案 1 :(得分:1)

我会保持简短:)

  1. 似乎它是ISerializable界面搞砸了。如果你删除它,一切都很完美。

  2. 您是否ISerializableEmployeeDepartment中实施了ISerializable界面?如果没有,只需将其删除即可。如果你这样做,GOTO 3;)

  3. 我不知道 Newtonsoft.Json 内部,但不知何故,在实施{{1}}时,它并没有正确处理循环引用。您应该在REALLY good reason提交问题。

答案 2 :(得分:1)

  

但是当循环引用属性Manager设置为NON NULL值时   然后它会在序列化字符串中被忽略,因此它会抛出一个   反序列化中的例外。

因为忽略了循环引用。这就是ReferenceLoopHandling.Ignore。

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

答案 3 :(得分:-1)

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

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

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

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

  2. 忽略违规财产

  3. 重组您的课程