2014-09-15 4 views
16

lista ho doppiamente collegato che sto cercando di deserialise.JSON .Net non rispettando PreserveReferencesHandling sulla deserializzazione

mio scenario è strettamente legata a questo SO: Doubly Linked List to JSON

ho le seguenti impostazioni JSON:

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

Quando guardo in uscita serializzato, sembra corretto, ed i riferimenti tra i nodi sono adeguatamente rappresentato.

Quando i dati sono deserialised, le proprietà principali nella oggetti figlio sono nulli, anche se sono popolati con $ rif correttamente.

Di seguito è riportato un esempio di JSON (tagliati per migliorare la leggibilità)

Nel processo di battitura a questa domanda - forse ho visto la fonte del problema ...

gli oggetti nel "Bambini "la proprietà dell'array non ha attributi di tipo $.

Ciò può essere perché i bambini e le proprietà Parent sono di tipo generico T.

noti che il tipo effettivo viene serializzato è una classe derivata di TemplateDataLinkedListBase

public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement> 

Ecco un estratto della base classe:

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

Come posso deserialise questo JSON in modo tale che la proprietà Parent non è nullo e contiene un riferimento a th e oggetto genitore?

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

Ecco i link Pastebin al codice relativo:

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

Puoi pubblicare l'intera definizione di classi per capire meglio cosa sta succedendo? –

+0

E anche un esempio di come si sta eseguendo la serializzazione e la deserializzazione? –

+0

Ciao @IlijaDimov Ho incluso i collegamenti al codice sorgente – RobD

risposta

15

Ecco quello che ho provato e ha funzionato bene:

Le classi

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

inizializzazione

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; 

impostazioni di serializzazione

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

serializzazione

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

JSON serializzato si presenta così:

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

deserializzazione

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

I riferimenti nella deserializedStructure sono conservati in modo corretto.

Demo https://dotnetfiddle.net/j1Qhu6

UPDATE 1

La ragione per il mio esempio funziona, e il codice che ha pubblicato un link supplementare non è perché le mie lezioni contengono costruttore di default, e il vostro don 't. Analizzando le tue classi, aggiungendo loro un costruttore predefinito, non interromperà la funzionalità e la deserializzazione avrà successo con la proprietà Parent inizializzata correttamente. Quindi quello che fondamentalmente dovete fare è aggiungere un costruttore di default per entrambe le classi:

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

Il EntityName proprietà che si imposta attraverso il vostro costruttore, saranno deserializzata correttamente, dal momento che è una proprietà pubblica.