簡體   English   中英

Json.net:序列化/反序列化不適用於具有循環引用的ISerializable對象

[英]Json.net: Serialisation/Deserialisation not working for ISerializable object that has circular reference

一段時間后我報告了一個問題 ,我在Json.net 4.5 R11中得到了修復。

如果我的循環引用屬性Manager為NULL,則序列化和反序列化工作正常。

但是當循環引用屬性Manager設置為NON NULL值時,它會在序列化字符串中被忽略,因此它會在反序列化中引發異常。

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<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

基於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. 你有一個很好的理由為實現ISerializable接口EmployeeDepartment 如果沒有,只需將其刪除即可。 如果你這樣做,GOTO 3;)

  3. 我不知道Newtonsoft.Json內部,但不知何故,它在實現ISerializable時沒有正確處理循環引用。 您應該在GitHub上提出問題。

但是當循環引用屬性管理器設置為NON NULL值時,它會在序列化字符串中被忽略,因此它會在反序列化中引發異常。

因為循環引用被忽略了。 這就是ReferenceLoopHandling.Ignore的要點。

PreserveReferencesHandling不適用於ISerializable,因為必須在父值之前創建子值。

看起來你真的不需要實現Iserializable接口。 這種類結構很簡單。 我已經序列化了非常大的類而不必走那么遠。

如果您仍有循環引用問題,請嘗試以下操作:

  1. 我相信JSON.net有一個格式化選項來忽略循環引用

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

  2. 忽略違規財產

  3. 重組你的課程

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM