2014-09-15 12 views
16

Mam podwójnie połączoną listę, którą próbuję deserialise.JSON .Net nie szanuje PreserveReferencesHandling na Deserializacji

Mój scenariusz ściśle dotyczy to SO: Doubly Linked List to JSON

Mam następujące ustawienia JSON:

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

Kiedy patrzę na wyjściu odcinkach wydaje się poprawne, a odniesienia między węzłami są prawidłowo reprezentowane.

Gdy dane są deserializowane, właściwości nadrzędne w obiektach podrzędnych mają wartość NULL, mimo że są wypełnione poprawką $ poprawnie.

Poniżej znajduje się próbka JSON (przyciętą dla czytelności)

W trakcie pisania tego pytania - mogę widzieli źródło kłopotów ...

obiektów w „Dzieci "Właściwość array nie ma atrybutów typu $.

To może być dlatego, że dzieci i zaawansowane właściwości są typu rodzajowy T.

Rzeczywiste typu są szeregowane jest klasa pochodną TemplateDataLinkedListBase

public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement> 

Tutaj fragment podstawy klasa:

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

Jak mogę deserialise ten JSON w taki sposób, że nieruchomość Dominująca nie jest null i zawiera odniesienie do th e obiekt nadrzędny?

{ 
    "$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": "" 
      }, 

Oto linki Pastebin do odpowiedniego kodu:

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

Czy możesz umieścić całą definicję klasy, aby lepiej zrozumieć, co się dzieje? –

+0

A także przykład sposobu przeprowadzania serializacji i deserializacji? –

+0

Witam @ IlijaDimov Zawarłem linki do kodu źródłowego – RobD

Odpowiedz

15

Oto co próbowałem i działało w porządku:

klasach

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>(); 
    } 
} 

inicjalizacji

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; 

ustawienia serializacji

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

serializacji

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

szeregowej json wygląda następująco:

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

Deserializacji

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

Odniesienia w deserializedStructure są zachowane prawidłowo.

Demo https://dotnetfiddle.net/j1Qhu6

UPDATE 1

Powodem mój przykład działa, a kod zostanie zaksięgowana w dodatkowych linków nie znaczy, ponieważ moje zajęcia zawierać domyślnego konstruktora i twój don "t. Analizowanie klas, dodawanie do nich domyślnego konstruktora, nie przerwie funkcjonalności, a deserializacja zakończy się pomyślnie z poprawną zainicjalizowaniem właściwości Parent. Więc co w zasadzie musisz zrobić, to dodać domyślnego konstruktora do obu klas:

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); 
    } 
} 

Obiekt EntityName które można ustawić za pośrednictwem konstruktora, będzie rozszeregować poprawnie, ponieważ jest to własność publiczną.