如何反序列化Newtonsoft Json.. NET引用单独的、单独的实例
本文关键字:单独 实例 引用 NET 反序列化 Newtonsoft Json | 更新日期: 2023-09-27 18:02:42
我有一段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<Person>>(json, jsonSerializerSettings);
但是这只是给了我一个包含Person的相同实例的数组,两次:
object.ReferenceEquals(deserializedPersons[0], deserializedPersons[1]) // Evaluates to true
我发现了一个我不满意的解决方案,它只是反序列化JSON字符串,然后使用上面的jsonSerializerSettings序列化它,这将复制JSON中的人,然后再反序列化。这将导致我们正在使用的大型对象的严重减速。
注:我知道我可以改变API,我检索这个JSON从中复制数据,但保留引用节省大量空间时,通过网络发送响应JSON
您可以使用自定义引用解析器。例如,假设Name
是对象的"主键",这应该可以工作。当然,你可能想让它更通用。
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;
}
}
现在你像这样反序列化:
var jsonSerializerSettings = new JsonSerializerSettings()
{
ReferenceResolver = new PersonReferenceResolver()
};
var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(
json, jsonSerializerSettings);
编辑:我很无聊,所以我做了一个通用版本:
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;
}
}
有一点新用法:
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);
$ref反序列化有问题。只有当元数据位于请求/响应的开头时,PreserveReferencesHandling才有帮助。否则我建议使用:
var settings = new JsonSerializerSettings();
settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore; //ign
var deserializedObjects = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(requestResult.Content, settings);
关于https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_MetadataPropertyHandling.htm
Default 0 Read metadata properties located at the start of a JSON object.
ReadAhead 1 Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance.
Ignore 2 Do not try to read metadata properties.