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

查看:68
本文介绍了如何反序列化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 反序列化到包含James人的两个副本的对象中.

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

当前,我正在使用此

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.

推荐答案

您可以使用

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天全站免登陆