如何将Newtonsoft Json.NET引用反序列化为单独的单个实例

我有一块看起来像这样的JSON:

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

正如你可以通过$ ref告诉的那样,这个JSON数组包含两次相同的Person(James)。 第二次是对第一次的引用。

我想知道是否有办法这个JSON 反序列化为一个包含两个詹姆斯人副本的对象。

目前,我正在使用这个:

 var jsonSerializerSettings = new JsonSerializerSettings() { PreserveReferencesHandling = PreserveReferencesHandling.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; var deserializedPersons = JsonConvert.DeserializeObject<List>(json, jsonSerializerSettings); 

但这只是给我一个数组,其中包含相同的Person实例,两次:

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

我发现了一个我不满意的解决方法,它只是反序列化JSON字符串,然后使用上面的jsonSerializerSettings序列化它,它将复制JSON中的人,然后再次反序列化它。 这导致我们正在使用的大型物体出现严重减速。

注意 :我知道我可以更改从检索此JSON的API以复制数据,但保留引用可以在通过线路发送响应JSON时节省大量空间。

您可以使用自定义参考解析器 。 例如,假设Name是对象的“主键”,这应该有效。 当然,您可能希望使其更通用。

 public class PersonReferenceResolver : IReferenceResolver { private readonly IDictionary _objects = new Dictionary(); 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; } } 

现在你像这样反序列化:

 var jsonSerializerSettings = new JsonSerializerSettings() { ReferenceResolver = new PersonReferenceResolver() }; var deserializedPersons = JsonConvert.DeserializeObject>( json, jsonSerializerSettings); 

编辑:我很无聊,所以我做了一个通用版本:

 public class GenericResolver : IReferenceResolver where TEntity : ICloneable, new() { private readonly IDictionary _objects = new Dictionary(); private readonly Func _keyReader; public GenericResolver(Func 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; } } 

略带新用法:

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