如何反序列化 Newtonsoft Json.NET 对单独实例的引用 [英] How to deserialize Newtonsoft Json.NET references to separate, individual instances

查看:22
本文介绍了如何反序列化 Newtonsoft Json.NET 对单独实例的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段如下所示的 JSON:

I have a piece of JSON that looks like this:

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

从 $ref 可以看出,这个 JSON 数组包含两次相同的 Person (James).第二次是对第一次的引用.

As you can tell by the $ref, this JSON array contains the same Person (James), twice. The second time is a reference to the first.

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

I am wondering if there is a way to deserialize this JSON into an object that contains two copies of the James person.

目前,我正在使用这个:

Currently, I'm using this:

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

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

但这只是给了我一个具有相同 Person 实例的数组,两次:

But this just gives me an array with the same instance of the Person, twice:

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

我找到了一个我不满意的解决方法,它只是反序列化 JSON 字符串,然后使用上面的 jsonSerializerSettings 对其进行序列化,这将复制 JSON 中的人,然后再次对其进行反序列化.这会导致我们正在使用的大型对象严重减速.

I've found a workaround I am unhappy with which is simply deserializing the JSON string, then serializing it using the jsonSerializerSettings above, which will duplicate the person in the JSON, then deserializing it again. This is causing major slowdowns for the large objects we are using.

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

Note: I know I could change the API that I retrieve this JSON from to duplicate the data, but preserving the references saves substantial space when sending the response JSON over the wire.

推荐答案

你可以使用 自定义参考解析器.例如,假设 Name 是您的对象的主键",这应该可以工作.当然,您可能想让它更通用.

You could use a custom reference resolver. For example, assuming Name is the "primary key" for your objects, this should work. Of course, you may want to make it more generic.

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

现在你像这样反序列化:

Now you deserialise like this:

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

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

<小时>

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


I was bored so I made a generic version:

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

这篇关于如何反序列化 Newtonsoft Json.NET 对单独实例的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆