2016-08-16 5 views
4

ich ein Stück von JSON, die wie folgt aussieht:Wie Newtonsoft Json.NET Verweise auf deserialisieren zu trennen, einzelne Instanzen

[ 
    { 
    "$id": "1", 
    "Name": "James", 
    "BirthDate": "1983-03-08T00:00Z", 
    "LastModified": "2012-03-21T05:40Z" 
    }, 
    { 
    "$ref": "1" 
    } 
] 

Wie Sie durch die $ ref sagen kann, das JSON-Array enthält die gleiche Person (James), zweimal. Das zweite Mal ist ein Verweis auf das erste Mal.

Ich frage mich, ob es eine Möglichkeit zu deserialize diese JSON in ein Objekt ist, das des James Person zwei Kopien enthält.

Derzeit bin ich mit diesem:

var jsonSerializerSettings = new JsonSerializerSettings() 
{ 
    PreserveReferencesHandling = PreserveReferencesHandling.None, 
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore 
}; 

var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(json, jsonSerializerSettings); 

Aber das gibt mir nur ein Array mit der gleichen Instanz der Person, zweimal:

object.ReferenceEquals(deserializedPersons[0], deserializedPersons[1]) // Evaluates to true 

ich eine Abhilfe gefunden habe ich bin unglücklich mit dem ist einfach die JSON-Zeichenfolge deserialisieren, dann Serialisierung es mit den obigen jsonSerializerSettings, die die Person in der JSON duplizieren, dann deserialisieren wieder. Dies verursacht große Verlangsamungen für die großen Objekte, die wir verwenden.

Hinweis: Ich weiß, ich könnte die API ändern, dass ich diesen JSON abrufen aus, die Daten zu duplizieren, aber den Verweises Erhaltung spart erheblichen Raum, wenn die Antwort JSON über den Draht zu senden.

+0

IF * verlangsamen * ist die WIRKLICHE Frage, verwenden Sie einfach [JSONFAST] (http://www.codeproject.com/Articles/159450/fastJSON) es ist doppelt so schnell wie James Newton-Kings-Implementierung. –

+1

Wäre es geeignet, über Ihre Personenliste zu iterieren und jede Person nach der Deserialisierung zu klonen? – recursive

+0

Ah, ich habe es falsch gelesen - ich dachte, er versuchte, die Refs loszuwerden – Plutonix

Antwort

2

Sie könnten eine custom reference resolver verwenden. Angenommen, Name ist der "Primärschlüssel" für Ihre Objekte, sollte dies funktionieren. Natürlich möchten Sie es vielleicht generischer machen.

public class PersonReferenceResolver : IReferenceResolver 
{ 
    private readonly IDictionary<string, Person> _objects = 
     new Dictionary<string, Person>(); 

    public object ResolveReference(object context, string reference) 
    { 
     Person p; 
     if (_objects.TryGetValue(reference, out p)) 
     { 
      //This is the "clever" bit. Instead of returning the found object 
      //we just return a copy of it. 
      //May be better to clone your class here... 
      return new Person 
      { 
       Name = p.Name, 
       BirthDate = p.BirthDate, 
       LastModified = p.LastModified 
      }; 
     } 

     return null; 
    } 

    public string GetReference(object context, object value) 
    { 
     Person p = (Person)value; 
     _objects[p.Name] = p; 

     return p.Name; 
    } 

    public bool IsReferenced(object context, object value) 
    { 
     Person p = (Person)value; 

     return _objects.ContainsKey(p.Name); 
    } 

    public void AddReference(object context, string reference, object value) 
    { 
     _objects[reference] = (Person)value; 
    } 
} 

Jetzt deserialise Sie wie folgt aus:

var jsonSerializerSettings = new JsonSerializerSettings() 
{ 
    ReferenceResolver = new PersonReferenceResolver() 
}; 

var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(
    json, jsonSerializerSettings); 

Edit: ich langweilig war, damit ich eine generische Version gemacht:

public class GenericResolver<TEntity> : IReferenceResolver 
    where TEntity : ICloneable, new() 
{ 
    private readonly IDictionary<string, TEntity> _objects = new Dictionary<string, TEntity>(); 
    private readonly Func<TEntity, string> _keyReader; 

    public GenericResolver(Func<TEntity, string> keyReader) 
    { 
     _keyReader = keyReader; 
    } 

    public object ResolveReference(object context, string reference) 
    { 
     TEntity o; 
     if (_objects.TryGetValue(reference, out o)) 
     { 
      return o.Clone(); 
     } 

     return null; 
    } 

    public string GetReference(object context, object value) 
    { 
     var o = (TEntity)value; 
     var key = _keyReader(o); 
     _objects[key] = o; 

     return key; 
    } 

    public bool IsReferenced(object context, object value) 
    { 
     var o = (TEntity)value; 
     return _objects.ContainsKey(_keyReader(o)); 
    } 

    public void AddReference(object context, string reference, object value) 
    { 
     if(value is TEntity) 
      _objects[reference] = (TEntity)value; 
    } 
} 

Mit leicht neue Nutzung:

var jsonSerializerSettings = new JsonSerializerSettings() 
{ 
    //Now we need to specify the type and how to get the object's key 
    ReferenceResolver = new GenericResolver<Person>(p => p.Name) 
}; 

var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(
    json, jsonSerializerSettings); 
+0

Ich bin gerade im Handy, aber ich werde das heute Abend bewerten! Scheint so, als müsste es funktionieren. – Tgeo

Verwandte Themen