JamesNK / Newtonsoft.Json

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

Incorrect deserialization when default constructor initialize a list of objects #2814

Open orinciog opened 1 year ago

orinciog commented 1 year ago

Hi! I'm trying to deserialize to an object(TestListObj) which contains a list of other object (List<TestObj>) The default constructor of the root object initializes the list.

The problem is that the deserialized object contains the list initialized from the constructor, not the object from serialization.

Source/destination types

public class TestObj
    {
        [JsonProperty("nr")]
        public float nr;
        public TestObj()
        {
            nr = 0;
        }
    }
  public class TestListObj
  { 
      public List<TestObj> model { get; set; }
      public TestListObj()
      {
         model = new List<TestObj>(10);
     model.Add(new TestObj());
     model.Add(new TestObj());
         model[0].nr = 2;
         model[1].nr = 3;
      }
}

Code to serialize/deserialize

//Test with list nested object : Deserialization does not work.
TestListObj listObj = new TestListObj
{
    model = new List<TestObj>
              {
                  new TestObj{ nr=1.7f }
              },
};
String jsonList = JsonConvert.SerializeObject(listObj);
TestListObj convListObj = JsonConvert.DeserializeObject<TestListObj>(jsonList);
Console.WriteLine(jsonList); //Good:{"model":[{"nr":1.7}]}
Console.WriteLine(convListObj.model[0].nr); //2 -> Incorrect 1.7 was serialized

Expected behavior

I expect the deserialized object to be the same as the serialized object

Actual behavior

The deserialized object contains the list initialized from the constructor.

Steps to reproduce

I created a working dotnetFiddle which shows the problem. In this fiddle I also put the deserialization to an object (TestSimpleObj) which contains only an object (TestObj) and it works ok.

I'm using Newtonsoft.JSON 13.0.2 library.

Thank you.

elgonzo commented 1 year ago

Not incorrect. It's the default for the ObjectCreationHandling setting in action. It's a bit unfortunate that this particular behavior has been chosen the default behavior, because it (unsurprisingly) catches quite a few users by surprise, but that choice had been made quite long ago, and with so many software projects relying on Newtonsoft.Json it is rather unlikely that a default setting will change... :(

To change the behavior to what you would expect, set the ObjectCreationHandling setting to ObjectCreationHandling.Replace.

See here for more details: https://github.com/JamesNK/Newtonsoft.Json/issues/2706#issuecomment-1183252408