Why can't Json.Net 6 deserialize T as a generic list in a dynamic object? (works in version 5)

I have updated from version 5 to 6.0.8 of Json.net. However, my code stops working and it took me a while to realize that in the previous version it was able to handle the case where the generic type was created using a list. But now it is not so. My question is, are there any settings that I need to tweak to get the old behavior?

In the old library class

 public class Result<T> 
    {
        public T Data
        {
            get;
            set;
        }

        public int ResultCode
        {
            get;
            set;
        }
    }

      

Will work for the case where the class was created with something like

Result<List<int>> result;

      

So, in my code, I was doing something like

dynamic result = JsonConvert.DeserializeObject(result, typeof(JObject));
int firstValue = (int)result.Data[0];

      

However with latest Json.Net it fails and throws an exception (Accessing JObject values ​​with invalid key value: 0. Object property name expected.). The only job I found is

var resultList = result.Data.ToObject<List<int>>();
int firstValue = resultList[0];

      

Obviously, this kind of hitting the target is dynamic, and I would rather go back to the old behavior. Anything I can do to tell Json.net that T can be List <int>? I can see the metadata is aware of this since the $ type and $ values ​​property are visible in the Result view for the dynamic object.

Any help is appreciated, thanks.

To help isolate the issue and answer the commenting question, I created a test application. It turns out that the TypeNameHandling.All parameter is causing the argument to be thrown. I added a comment to the code to show good / bad serialization. And yes, I think we need the All option in our code.

    using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShowingBug
{
    public class InterestingResult
    {
        public string Information { get; set; }
    }

    public class Result<T> 
    {
        public T Data { get; set; }
        public int ResultCode { get; set; }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var veryInteresting = new List<InterestingResult>();
            veryInteresting.Add(new InterestingResult() { Information = "Good" });

            Result<List<InterestingResult>> operationResult = new Result<List<InterestingResult>>();
            operationResult.Data = veryInteresting;
            operationResult.ResultCode = 1;

            JsonSerializerSettings serializerSettings = new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto
            };

            var json = JsonConvert.SerializeObject(operationResult, serializerSettings);
            dynamic result = JObject.Parse(json);
            string information = (string)result.Data[0].Information;

            Console.Out.WriteLine(information);

            //The above works, however after some digging... 
            // I found that the option we use is not Auto, but All

            serializerSettings = new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.All
            };

            json = JsonConvert.SerializeObject(operationResult, serializerSettings);
            result = JObject.Parse(json);

            // Now, you get an ArgumentException from Json.net 6.0.8 on the following line
            information = (string)result.Data[0].Information; 

            // It worked in previous version of Json.Net
            // I'm using .net 4.5.2 (if that makes a difference)
        }
    }
}

      

+3


source to share


1 answer


I don't see an error here. The problem is that you seem to be expecting to return your original object Result<T>

even if you specify that the type of the deserialized object should be JObject

. This will never work because it Result<T>

does not JObject

and JObject

cannot contain custom objects such as Result<T>

. A JObject

can only contain a set of objects JProperty

, and they can only contain values ​​derived from JToken

. This is by design.

When you call JsonConvert.DeserializeObject(json, typeof(JObject))

it makes the returned object be JObject

even if the receiving variable is declared as dynamic

. Likewise, JObject.Parse()

will always return JObject

.



If you want to get your original objects back while respecting the built-in type information in JSON, you should use the typeless overload JsonConvert.DeserializeObject()

, passing it the same JsonSerializationSettings

one you used to serialize:

JsonSerializerSettings serializerSettings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};

dynamic result = JsonConvert.DeserializeObject(json, serializerSettings);

      

0


source







All Articles