2014-09-15 15 views
16

Tôi có danh sách liên kết kép mà tôi đang cố gắng deserialise.JSON .Net không tôn trọng PreserveReferencesHandling on Deserialization

kịch bản của tôi liên quan chặt chẽ với SO này: Doubly Linked List to JSON

tôi có các thiết lập JSON sau:

_jsonSettings = new JsonSerializerSettings() 
{ 
    TypeNameHandling = TypeNameHandling.Auto, 
    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, 
    PreserveReferencesHandling = PreserveReferencesHandling.Objects, 
    ObjectCreationHandling = ObjectCreationHandling.Auto 
}; 

Khi tôi nhìn vào đầu ra tuần tự, nó xuất hiện đúng, và tài liệu tham khảo giữa các nút là đúng đại diện.

Khi dữ liệu được deserialised, các thuộc tính cha mẹ trong các đối tượng con là null, mặc dù chúng được điền với $ ref một cách chính xác.

Dưới đây là một mẫu của các JSON (tỉa để có thể đọc)

Trong quá trình gõ câu hỏi này - tôi có thể đã thấy nguồn gốc của những rắc rối ...

Các đối tượng trong "Trẻ em "thuộc tính mảng không có thuộc tính type $.

này có thể là do trẻ em và tài sản Chánh là của chung loại T.

Lưu ý rằng các loại thực tế được đăng là một lớp dẫn xuất của TemplateDataLinkedListBase

public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement> 

Dưới đây là một đoạn trích của các cơ sở lớp học:

public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T> 
{ 
    [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)] 
    public T Parent { get; set; } 

    [JsonProperty(TypeNameHandling=TypeNameHandling.Objects)] 
    public List<T> Children { get; set; } 
} 

Làm thế nào tôi có thể deserialise JSON này theo cách mà thuộc tính Parent không rỗng và chứa tham chiếu đến thứ e đối tượng cha mẹ?

{ 
    "$id": "9", 
    "$type": "Contracts.Models.TemplateDataQueryElement, Contracts", 
    "Query": null, 
    "Parent": null, 
    "Children": [ 
     { 
     "$id": "11", 
     "Query": null, 
     "Parent": { 
      "$ref": "9" 
     }, 
     "Children": [ 
      { 
      "$id": "13", 
      "Query": null, 
      "Parent": { 
       "$ref": "11" 
      }, 
      "Children": [], 
      "EntityName": "Widgets", 
      "Fields": [ 
       "Id" 
      ], 
      "Key": "" 
      }, 

Dưới đây là Pastebin liên kết đến các mã có liên quan:

http://pastebin.com/i1jxVGG3 http://pastebin.com/T1xqEWW2 http://pastebin.com/ha42SeF7 http://pastebin.com/cezwZqx6 http://pastebin.com/uFbTbUZe http://pastebin.com/sRhNQgzh

+0

Bạn có thể đăng định nghĩa toàn bộ lớp học để hiểu rõ hơn về những gì đang diễn ra không? –

+0

Và cũng là một ví dụ về cách bạn đang thực hiện tuần tự hóa và deserialization? –

+0

Xin chào @IlijaDimov Tôi đã bao gồm các liên kết đến mã nguồn – RobD

Trả lời

15

Đây là những gì tôi đã cố gắng và làm việc tốt:

Các lớp

public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T> 
{ 
    [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)] 
    public T Parent { get; set; } 

    [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)] 
    public List<T> Children { get; set; } 
} 

public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement> 
{ 
    public string Query { get; set; } 

    public TemplateDataQueryElement() 
    { 
     Children = new List<TemplateDataQueryElement>(); 
    } 
} 

Khởi

var childLowest = new TemplateDataQueryElement 
{ 
    Query = "Lowest" 
}; 

var childMiddle = new TemplateDataQueryElement 
{ 
    Query = "Middle", 
    Children = new List<TemplateDataQueryElement> 
    { 
     childLowest 
    } 
}; 

childLowest.Parent = childMiddle; 

var parent = new TemplateDataQueryElement 
{ 
    Query = "Parent", 
    Children = new List<TemplateDataQueryElement> 
    { 
     childMiddle 
    } 
}; 

childMiddle.Parent = parent; 

thiết lập serialization

var _jsonSettings = new JsonSerializerSettings() 
{ 
    TypeNameHandling = TypeNameHandling.Auto, 
    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, 
    PreserveReferencesHandling = PreserveReferencesHandling.Objects, 
    ObjectCreationHandling = ObjectCreationHandling.Auto 
}; 

serialization

var serializedStr = JsonConvert.SerializeObject(parent, Formatting.Indented, _jsonSettings); 

Các json serialized như sau:

{ 
    "$id": "1", 
    "Query": "Parent", 
    "Parent": null, 
    "Children": [ 
    { 
     "$id": "2", 
     "Query": "Middle", 
     "Parent": { 
     "$ref": "1" 
     }, 
     "Children": [ 
     { 
      "$id": "3", 
      "Query": "Lowest", 
      "Parent": { 
      "$ref": "2" 
      }, 
      "Children": [] 
     } 
     ] 
    } 
    ] 
} 

Deserialization

var deserializedStructure = JsonConvert.DeserializeObject<TemplateDataQueryElement>(serializedStr, _jsonSettings); 

Các tài liệu tham khảo trong deserializedStructure được bảo tồn một cách chính xác.

Demo https://dotnetfiddle.net/j1Qhu6

UPDATE 1

Lý do ví dụ của tôi hoạt động, và mã bạn được đăng trong liên kết bổ sung không là bởi vì lớp học của tôi chứa constructor mặc định, và bạn don 't. Phân tích các lớp của bạn, thêm một hàm tạo mặc định cho chúng, nó sẽ không phá vỡ chức năng và quá trình deserialization sẽ thành công với thuộc tính Parent được khởi tạo chính xác. Vì vậy, những gì về cơ bản bạn cần làm là thêm một constructor mặc định để cả hai lớp:

public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T> 
{ 
    [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)] 
    public T Parent { get; set; } 

    [JsonProperty(TypeNameHandling=TypeNameHandling.Objects)] 
    public List<T> Children { get; set; } 
    public string EntityName { get; set; } 
    public HashSet<string> Fields { get; set; } 

    public string Key { get { return getKey(); } } 


    public TemplateDataLinkedListBase() 
    { 
     Children = new List<T>(); 
     Fields = new HashSet<string>(); 
    } 

    public TemplateDataLinkedListBase(string entityName) 
    { 
     EntityName = entityName; 
     Children = new List<T>(); 
     Fields = new HashSet<string>(); 
    } 

    private string getKey() 
    { 
     List<string> keys = new List<string>(); 
     keys.Add(this.EntityName); 
     getParentKeys(ref keys, this); 
     keys.Reverse(); 
     return string.Join(".", keys); 

    } 

    private void getParentKeys(ref List<string> keys, TemplateDataLinkedListBase<T> element) 
    { 
     if (element.Parent != null) 
     { 
      keys.Add(element.Parent.EntityName); 
      getParentKeys(ref keys, element.Parent); 
     } 
    } 

    public T AddChild(T child) 
    { 
     child.Parent = (T)this; 
     Children.Add(child); 
     return (T)this; 
    } 

    public T AddChildren(List<T> children) 
    { 
     foreach (var child in children) 
     { 
      child.Parent = (T)this; 
     } 
     Children.AddRange(children); 
     return (T)this; 
    } 

    public void AddFields(IEnumerable<string> fields) 
    { 
     foreach (var field in fields) 
      this.Fields.Add(field); 
    } 

    public TemplateDataLinkedListBase<T> Find(string searchkey) 
    { 
     if (this.Key == searchkey) 
     { 
      return this; 
     } 
     else 
     { 
      foreach (var child in Children) 
      { 
       if (child.Key == searchkey) 
       { 
        return child; 
       } 
       else 
       { 
        var childResult = child.Find(searchkey); 
        if (childResult != null) return childResult; 
       } 
      } 
     } 
     return null; 
    } 
} 

public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>, ITemplateDataQueryElement 
{ 
    public string TemplateModelName { get; set; } 
    public string RecordId { get; set; } 
    public string ParentForeignKeyName { get; set; } 
    public string Query { get; set; } 
    public dynamic ObjectData { get; set; } 
    public ITemplateDataParseResult ParseResult { get; set; } 


    public TemplateDataQueryElement() : base() 
    { 
     Fields.Add("Id"); //Always retrieve Id's 
     ObjectData = new ExpandoObject(); 
    } 

    public TemplateDataQueryElement(string entityName) 
     : base(entityName) 
    { 
     Fields.Add("Id"); //Always retrieve Id's 
     ObjectData = new ExpandoObject(); 
    } 

    public override string ToString() 
    { 
     return string.Format("{0}: {1}", EntityName, Query); 
    } 
} 

Các EntityName tài sản mà bạn thiết lập thông qua constructor của bạn, sẽ được deserialized một cách chính xác, vì nó là một tài sản công cộng.

Các vấn đề liên quan