Search code examples
c#parsingjson.netbigintegeruint64

Json.net DeserializeAnonymousType returns BigInteger instead of UInt64


Suppose we have the following code:

Type t = typeof(UInt64);
var def = Activator.CreateInstance(t);
Console.WriteLine(def.GetType());

string json = "18446744073709551615";
object parsedObject = JsonConvert.DeserializeAnonymousType(json, def);
Console.WriteLine(parsedObject.GetType());

The output is:

System.UInt64 
System.Numerics.BigInteger

Since I explicitly fed DeserializeAnonymousType with def, type of which is UInt64, I would expect also UInt64 as an output, especially since 18446744073709551615 is a valid UInt64 value (max value, granted, but I checked also with 18446744073709551614, it is same).

Why is it so? I checked Newtonsoft Github issues, and found nothing (at least, not in open issues).

How can I force the correct type which I know in advance (dynamically, I have only System.Type)? Obviously, if it happens only for UInt64 I can make a check and cast explicitly.


Solution

  • Your problem is that JsonConvert.DeserializeAnonymousType<T>(string value, T anonymousTypeObject) doesn't quite do what you expect. It doesn't deserialize the JSON string to the actual type of the T anonymousTypeObject argument. It deserializes the JSON string to the declared type of the argument -- which in your case, is object.

    This can be seen from the reference source:

    public static T DeserializeAnonymousType<T>(string value, T anonymousTypeObject)
    {
        return DeserializeObject<T>(value);
    }
    

    The T anonymousTypeObject is never actually used. Instead the target type is entirely specified via type inference when the call to DeserializeAnonymousType is made -- specifically from the declared type of the variable def in your code. But def is implicitly declared to be an object, because that is what Activator.CreateInstance(Type type) is declared to return. Thus object is what Json.NET deserializes the JSON as -- some .Net type sufficient to capture the incoming JSON. As BigInteger is sufficient to capture the incoming JSON, Newtonsoft happens to choose that type instead of Uint64.

    if you want to force Json.NET to deserialize to the actual, concrete type of a specific object, use JsonConvert.DeserializeObject(string value,Type type):

    object parsedObject = JsonConvert.DeserializeObject(json, def.GetType());
    

    Demo fiddle here.