JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.71k stars 3.24k forks source link

Deserialize `null` to struct type property with JsonConverter throw no exception in .NET 6 #2896

Open heku opened 11 months ago

heku commented 11 months ago

Source/destination types

public class TestCalss
{
    public NonNullType Value { get; set; }
}

public struct NonNullType
{
}

Source/destination JSON

{"Value":null}

Expected behavior

.NET Framework 4.8: Newtonsoft.Json.JsonSerializationException: Error setting value to 'Value' on 'Program+TestCalss'.

Actual behavior

No exception in .NET 6.

Steps to reproduce

The code below throw exception in .NET Framework 4.8 (as expected), but throw no exception in .NET 6.


// <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

using Newtonsoft.Json;
using System;

internal class Program
{
    public class TestCalss
    {
        public NonNullType Value { get; set; }
    }
    public struct NonNullType
    {
    }

    public class MyJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => objectType == typeof(NonNullType);

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => null;

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { }
    }

    private static void Main(string[] args)
    {
        var json = "{\"Value\":null}";

        var c = JsonConvert.DeserializeObject<TestCalss>(json, new MyJsonConverter());

        Console.WriteLine("DONE");
        Console.ReadKey();
    }
}
heku commented 11 months ago

image

elgonzo commented 11 months ago

The code below throw exception in .NET Framework 4.8 (as expected), but throw no exception in .NET 6.

Well, dotnetfiddle has no Framework 4.8 target, but it has a Framework 4.7.2 target. And your code does NOT throw in 4.7.2: https://dotnetfiddle.net/ItXTNG (the dotnetfiddle is using Newtonsoft.Json 13.0.3.)

But there is something unusual going on in your 4.8 exception stack trace. Note that there is a DynamicValueProvider involved that should not be involved if you were to actually execute the code as in your report.

I modified your code example so the Value property setter throws an exception to see the actual stacktrace when setting the property fails: https://dotnetfiddle.net/ikz2p5 Note that there is no DynamicValueProvider appearing in the stacktrace.

This makes me believe that the 4.8 code you are running is different from the one in your report or using some old Newtonsoft.Json version. Make sure your 4.8 build target uses the current Newtonsoft.Json version (13.0.3 as of writing) and the code you test is as in your report. Clean and rebuild your solution. If the problem then still persists, could you please share the project (by attaching a zip file with the project to a github comment) that contains the code in your question and builds an 4.8 executable causing the exception you see?

heku commented 11 months ago

Thank you @elgonzo , here's the code. JsonNetBugDemo.zip

And here's my test result.

image

heku commented 11 months ago

Also, I tried changing the framework version to 4.7.2, and can get same exception. I can also confirm that I can reproduce the issue on different computer.

heku commented 11 months ago

Looks like the https://dotnetfiddle.net/bw3oKz is using a old Json.NET version 11.

4.0.30319.42000
Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
DONE
elgonzo commented 11 months ago

Looks like the https://dotnetfiddle.net/bw3oKz is using a old Json.NET version 11.

Oh dang. Despite selecting 13.0.2, dotnetfiddle still uses 11.0.x. for ye'olde Framework targets. Thanks for noticing.

I can reproduce the issue on my end (using with the simple settings as in your example project), both with the current 13.0.3 and also with the old 11.0.2 dotnetfiddle insisted on using. There is indeed a difference in behavior between the Newtonsoft.Json platform flavours for the old Framework 4.x targets and the .NETStandard targets offered by the Newtonsoft.Json nuget package.

(Dotnetfiddle properly pulled the wool over my eyes here; not only did it use 11.0.x instead of the selected 13.0.2, it also apparently seems to use one of the Newtonsoft.Json netstandard flavours instead of the net45 flavour the build tool would select. Aargh...)