1

I have a configuration-related data model which is required to have default values for all properties to ensure proper execution of consuming operations. Take for example this very simple model:

public class Sample {
    public List<int> Integers { get; set; }
    public Sample() =>
        Integers = new List<int> { 1 };
}

This model is expected to contain at least one value so we specify a default processing value based on predefined requirements. However, the value doesn't have to be 1 specifically and if a client specifies a different value or many different values, our process should respect their input. Assume the client specifies the following json configuration to be deserialized into the model:

{
    "integers": [ 2, 3 ]
}

During runtime, we load the configuration directly, but let's use a local string variable for the sake of this question:

using Newtonsoft.Json
...
string configuration = "{ \"integers\": [ 2, 3 ] }";
var sample = JsonConvert.DeserializeObject<Sample>(configuration);
Console.WriteLine(string.Join(",", sample.Integers));

The above code snippet should produce an output of:

1,2,3

As you can see in my screenshot below, that is the case: Screenshot showing the previous code snippets along with an overlay of a console window displaying the aforementioned output.

However, my question is why... Why does the deserialization process append to the collection instead of overwriting it?

Hazel へいぜる
  • 2,751
  • 1
  • 12
  • 44
  • Well, it respects your code in the constructor. You should know why you initialize the list with values, the serializer does not care about it. – Sir Rufo Jan 08 '23 at 23:40
  • 1
    @SirRufo I'd imagine the serializer would overwrite it by the same logic of not caring. This issue doesn't occur with `string` properties for example. – Hazel へいぜる Jan 08 '23 at 23:42
  • *Why does the deserialization process append to the collection instead of overwriting it?* Because James Newton-King designed it that way. Json.NET is designed to support populating existing instances via `JsonConvert.PopulaterObject()`, and this is one side effect. It has been this way for a decade or longer, see e.g. [Dublicate items in list #90](https://github.com/JamesNK/Newtonsoft.Json/issues/90) from 2013 which was closed with the comment *Use ObjectCreationHandling.Replace*. – dbc Jan 09 '23 at 03:20
  • If you don't like that there are many workarounds. Use `ObjectCreationHandling.Replace` from Serge's answer. Use a custom contract resolver or converter that resizes collections before populating them as shown in [Populate object where objects are reused and arrays are replaced?](https://stackoverflow.com/q/42165648/3744182). But the question of ***why*** James Newton-King chose to do it this way is borderline off-topic for Stack Overflow. (And in fact he might choose to do it differently after a decade of complaints.) – dbc Jan 09 '23 at 03:22
  • @dbc While I'm grateful for your response, and it was very helpful, I'd like to clarify that my question wasn't ***why*** James Newton-King chose to write the deserialization process the way he did, my question was ***why does the deserialization process append to the list instead of overwriting it***, which I believe is on topic for Stack Overflow, very explicitly stated in my post, and has been adequately answered by Serge. – Hazel へいぜる Jan 09 '23 at 03:36

1 Answers1

1

You can point how to deserialize the json

var serializerSettings = new JsonSerializerSettings {
ObjectCreationHandling = ObjectCreationHandling.Replace};

var sample = JsonConvert.DeserializeObject<Sample>(configuration,serializerSettings);

Console.WriteLine(string.Join(",", sample.Integers)); // [2,3]

by default it is auto, and if there is something it tries to add.

Serge
  • 40,935
  • 4
  • 18
  • 45