How to deserialize Newtonsoft Json.NET references to separate, individual instances

前端 未结 2 1013
借酒劲吻你
借酒劲吻你 2021-01-13 05:59

I have a piece of JSON that looks like this:

[
  {
    \"$id\": \"1\",
    \"Name\": \"James\",
    \"BirthDate\": \"1983-03-08T00:00Z\",
    \"LastModified\         


        
相关标签:
2条回答
  • 2021-01-13 06:40

    I had problem with $ref deserialization. PreserveReferencesHandling helps only if you have metadata located at the beginning of the request/response. Otherwise I recommend using those:

    var settings = new JsonSerializerSettings();
    settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; 
    settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore; //ign
    var deserializedObjects = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(requestResult.Content, settings);
    

    Regarding to 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.
    
    0 讨论(0)
  • 2021-01-13 06:50

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

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

    With slightly new usage:

    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 讨论(0)
提交回复
热议问题