简体   繁体   中英

Different behaviour of Newtonsoft.Json on .NET 5 / Core and .NET Framework

The following program results in different outcomes when run on .NET 5 (or .NET Core) and .NET Framework.

Why is the behaviour different? I'm not after the solution to the deserialisation problem; my goal here is to understand what happens.

class Versioned 
{
    public Version V {get; set;} = new Version(1,0);
}

static void Main(string[] args)
{
    // Serialised with version Newtonsoft.Json 9.0.1
    var json = "{\"V\":{\"Major\":2,\"Minor\":0,\"Build\":-1,\"Revision\":-1,\"MajorRevision\":-1,\"MinorRevision\":-1}}";

    Console.WriteLine($".NET: {System.Environment.Version}");
    Console.WriteLine($"Json.NET: {System.Reflection.Assembly.GetAssembly(typeof(Newtonsoft.Json.JsonConvert))}");

    Console.WriteLine(json);
    
    try 
    {
        var b = Newtonsoft.Json.JsonConvert.DeserializeObject<Versioned>(json);
        Console.WriteLine(b.V);
    } 
    catch (Exception ex) { Console.WriteLine(ex.GetBaseException().Message); }
}

On .NET 5: the output is (formatted for readability):

.NET: 5.0.1
Json.NET: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
{
    "V": {
        "Major": 2,
        "Minor": 0,
        "Build": -1,
        "Revision": -1,
        "MajorRevision": -1,
        "MinorRevision": -1
    }
}

With this error:

Cannot deserialize the current JSON object (eg {"name":"value"}) into type 'System.Version' because the type requires a JSON string value to deserialize correctly.

To fix this error either change the JSON to a JSON string value or change the deserialized type so that it is a normal .NET type (eg not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'V.Major', line 1, position 14.

On .NET Framework 4.6.2 the output is:

.NET: 4.0.30319.42000
Json.NET: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
{
    "V": {
        "Major": 2,
        "Minor": 0,
        "Build": -1,
        "Revision": -1,
        "MajorRevision": -1,
        "MinorRevision": -1
    }
}
1.0

Additionally, with NO default value the behaviour on .NET 4.6.2 is different again.

class Versioned
{
    public Version V { get; set; }// = new Version(1, 0);
}

On .NET 5.0 the output is the same:

(...)
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Version' because...

On .NET Framework 4.6.2 the output now is:

(...)
Version's parameters must be greater than or equal to zero.
Parameter name: build

As far as I know, the System.Version class has not changed (on the outside) between these .NET versions in a way that should play a part here (I know only of the ISpanFormattable difference). I had a peek at the source ( .NET Core version and .NET Framework 4.8 version ) but I cannot see anything that would explain the different behaviours.

It looks like Json.NET got a new VersionConverter in .NET Core 2.2, which knows how to properly serialize and deserialize Version instances. This automatically gets picked up and used when you're using .NET Core 2.2+.

Using VersionConverter , Json.NET wants to serialize your Version objects to strings like "1.0" , rather than to JSON objects. If you create the json string in your post by serializing a new instance of Versioned on .NET Core 2.2+:

var json = Newtonsoft.Json.JsonConvert.SerializeObject(new Versioned());

You'll see that it returns eg {"V":"1.0"} .

Likewise, VersionConverter only knows how to read version strings such as "1.0" , and doesn't know what to do with an object containing Major , Minor , etc.

See this issue for more . See code running here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM