2014-05-04 12 views
42

sto guardando un campione WebAPI applicazione che ha questo codice:Qual è la differenza tra PreserveReferencesHandling e ReferenceLoopHandling in Json.Net?

json.SerializerSettings.PreserveReferencesHandling 
    = Newtonsoft.Json.PreserveReferencesHandling.Objects; 

e un altro con questo codice:

json.SerializerSettings.ReferenceLoopHandling 
    = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

Né spiega perché ciascuna è scelto. Sono molto nuovo a WebAPI, quindi qualcuno può aiutarmi spiegandomi in termini semplici quali sono le differenze e perché potrei aver bisogno di usarne una sull'altra.

+4

Cosa ha detto la documentazione? Cosa non era chiaro a riguardo? – nvoigt

risposta

98

Queste impostazioni possono essere meglio illustrate con l'esempio. Diciamo che vogliamo rappresentare una gerarchia di dipendenti in un'azienda. Così facciamo una semplice classe come questa:

class Employee 
{ 
    public string Name { get; set; } 
    public List<Employee> Subordinates { get; set; } 
} 

Si tratta di una piccola azienda con soli tre dipendenti finora: Angela, Bob e Charles. Angela è il capo, mentre Bob e Charles sono i suoi subordinati. Impostiamo il backup dei dati per descrivere questo rapporto:

Employee angela = new Employee { Name = "Angela Anderson" }; 
Employee bob = new Employee { Name = "Bob Brown" }; 
Employee charles = new Employee { Name = "Charles Cooper" }; 

angela.Subordinates = new List<Employee> { bob, charles }; 

List<Employee> employees = new List<Employee> { angela, bob, charles }; 

Se serializzare l'elenco dei dipendenti a JSON ...

string json = JsonConvert.SerializeObject(employees, Formatting.Indented); 
Console.WriteLine(json); 

... otteniamo questo output:

[ 
    { 
    "Name": "Angela Anderson", 
    "Subordinates": [ 
     { 
     "Name": "Bob Brown", 
     "Subordinates": null 
     }, 
     { 
     "Name": "Charles Cooper", 
     "Subordinates": null 
     } 
    ] 
    }, 
    { 
    "Name": "Bob Brown", 
    "Subordinates": null 
    }, 
    { 
    "Name": "Charles Cooper", 
    "Subordinates": null 
    } 
] 

Fin qui tutto bene. Noterai, tuttavia, che le informazioni per Bob e Charles vengono ripetute nel JSON perché gli oggetti che li rappresentano sono referenziati sia dalla lista principale dei dipendenti che dalla lista dei subordinati di Angela. Forse è OK per ora.

Ora supponiamo di avere anche un modo per tenere traccia del supervisore di ciascun dipendente in aggiunta ai suoi subordinati. Quindi cambiamo il nostro modello Employee per aggiungere una proprietà Supervisor ...

class Employee 
{ 
    public string Name { get; set; } 
    public Employee Supervisor { get; set; } 
    public List<Employee> Subordinates { get; set; } 
} 

... e aggiungere un paio di righe al nostro codice di setup per indicare che Charles e Bob rapporto per Angela:

Employee angela = new Employee { Name = "Angela Anderson" }; 
Employee bob = new Employee { Name = "Bob Brown" }; 
Employee charles = new Employee { Name = "Charles Cooper" }; 

angela.Subordinates = new List<Employee> { bob, charles }; 
bob.Supervisor = angela;  // added this line 
charles.Supervisor = angela; // added this line 

List<Employee> employees = new List<Employee> { angela, bob, charles }; 

Ma ora abbiamo un po 'di problemi. Poiché il grafico dell'oggetto contiene dei loop di riferimento (ad esempio angela riferimenti bob mentre bob riferimenti angela), si otterrà un JsonSerializationException quando proviamo a serializzare la lista dei dipendenti. Un modo siamo in grado di ovviare a questo problema è impostando ReferenceLoopHandling a Ignore in questo modo:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 
    Formatting = Formatting.Indented 
}; 

string json = JsonConvert.SerializeObject(employees, settings); 

Con questa impostazione in atto, otteniamo la seguente JSON:

[ 
    { 
    "Name": "Angela Anderson", 
    "Supervisor": null, 
    "Subordinates": [ 
     { 
     "Name": "Bob Brown", 
     "Subordinates": null 
     }, 
     { 
     "Name": "Charles Cooper", 
     "Subordinates": null 
     } 
    ] 
    }, 
    { 
    "Name": "Bob Brown", 
    "Supervisor": { 
     "Name": "Angela Anderson", 
     "Supervisor": null, 
     "Subordinates": [ 
     { 
      "Name": "Charles Cooper", 
      "Subordinates": null 
     } 
     ] 
    }, 
    "Subordinates": null 
    }, 
    { 
    "Name": "Charles Cooper", 
    "Supervisor": { 
     "Name": "Angela Anderson", 
     "Supervisor": null, 
     "Subordinates": [ 
     { 
      "Name": "Bob Brown", 
      "Subordinates": null 
     } 
     ] 
    }, 
    "Subordinates": null 
    } 
] 

Se si esamina la JSON, è dovrebbe essere chiaro cosa fa questa impostazione: ogni volta che il serializzatore incontra un riferimento a un oggetto che è già in fase di serializzazione, salta semplicemente quel membro. (Questo impedisce al serializzatore di entrare in un ciclo infinito.) Puoi vedere che nella lista dei subordinati di Angela nella parte superiore del JSON, né Bob né Charles mostrano un supervisore. Nella parte inferiore del JSON, Bob e Charles mostrano entrambi Angela come loro supervisore, ma notano che la lista dei suoi subordinati a quel punto non include sia Bob che Charles.

Mentre è possibile lavorare con questo JSON e forse anche ricostruire la gerarchia dell'oggetto originale con un po 'di lavoro, non è chiaramente ottimale. Siamo in grado di eliminare le informazioni ripetute in JSON, pur conservando i riferimenti agli oggetti usando la PreserveReferencesHandling impostazione invece:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    PreserveReferencesHandling = PreserveReferencesHandling.Objects, 
    Formatting = Formatting.Indented 
}; 

string json = JsonConvert.SerializeObject(employees, settings); 

Ora otteniamo la seguente JSON:

[ 
    { 
    "$id": "1", 
    "Name": "Angela Anderson", 
    "Supervisor": null, 
    "Subordinates": [ 
     { 
     "$id": "2", 
     "Name": "Bob Brown", 
     "Supervisor": { 
      "$ref": "1" 
     }, 
     "Subordinates": null 
     }, 
     { 
     "$id": "3", 
     "Name": "Charles Cooper", 
     "Supervisor": { 
      "$ref": "1" 
     }, 
     "Subordinates": null 
     } 
    ] 
    }, 
    { 
    "$ref": "2" 
    }, 
    { 
    "$ref": "3" 
    } 
] 

Si noti che ora ogni oggetto è stato assegnato un valore sequenziale $id nel JSON. La prima volta che viene visualizzato un oggetto, viene serializzato per intero, mentre i riferimenti successivi vengono sostituiti con una speciale proprietà $ref che fa riferimento all'oggetto originale con il corrispondente $id. Con questa impostazione, il JSON è molto più conciso e può essere deserializzato nella gerarchia dell'oggetto originale senza lavoro aggiuntivo, supponendo che si stia utilizzando una libreria che comprende la notazione $id e prodotta da Json.Net/Web API.

Quindi, perché dovresti scegliere un'impostazione o l'altra? Dipende dalle tue esigenze, naturalmente. Se il JSON sarà consumato da un client che non comprende il formato $id/$ref e può tollerare di avere dati incompleti in alcuni punti, scegliere di utilizzare ReferenceLoopHandling.Ignore. Se stai cercando un JSON più compatto e utilizzerai Json.Net o Web API (o un'altra libreria compatibile) per deserializzare i dati, allora sceglieresti di usare PreserveReferencesHandling.Objects. Se i tuoi dati sono un grafico aciclico diretto senza riferimenti duplicati, non hai bisogno di nessuna impostazione.

+9

Ottima risposta. Quando si utilizza l'ultimo metodo ('PreserveReferencesHandling.Objects') [JsonNetDecycle] (https://bitbucket.org/smithkl42/jsonnetdecycle) è un'incredibile libreria per il riassemblaggio dei riferimenti dell'oggetto sul lato client. – WimpyProgrammer

+0

È davvero una buona risposta. Sono illuminato. –

Problemi correlati