0

I have a class with some properties, one of them a list of items. All of them are initialized in the default parameterless constructor of the class. I only want to have to have this constructor that initalizes everything.

This is a case for many of my classes.

public class ExampleClass
{
    public ExampleClass()
    {
        this.ListProperty = new List<int> { 1, 2 };
        this.APropertyToBeNull = new TypeA();
        this.BPropertyToBeNull = new TypeB();
    }

    public List<int> ListProperty { get; set; }

    public TypeA APropertyToBeNull { get; set; }

    public TypeB BPropertyToBeNull { get; set; }
}

I create an instance of this class, I set some of the properties to null and I modify the list property to have 2 items.

  var instance = new ExampleClass();
  instance.APropertyToBeNull = null;
  instance.BPropertyToBeNull = null;
  instance.ListProperty = new List<int> { 3 };

  var raw = JsonConvert.SerializeObject(instance);            
  var deserialized = JsonConvert.DeserializeObject<ExampleClass>(raw, settings);

  Assert.AreEqual(1, deserialized.ListProperty.Count);
  Assert.IsNull(deserialized.APropertyToBeNull);
  Assert.IsNull(deserialized.BPropertyToBeNull);

When I deserialize, I don't find a way of getting the item exactly as I serialized it. I got two options:

  1. If I set the ObjectCreationHandling to Replace, the list deserializes fine, but the null properties are not null anymore. The constructor initialized everything, the deserialization replaced the list completely but it did not set the null properties to null.
  2. If I set the ObjectCreationHandling to Auto or Reuse, the null properties are deserialized fine as null, but the list has the items that were initialized in the constructor plus the items in the JSON. I only want those in the JSON.

How do I get the exact same item I serialized without removing the initialization of all the properties in the constructor (I still want to have all of them initialized in case a new instance is created).

I have played with all possible settings of the serializer, and I don't find a solution.

As a further constraint, I don't want to add attributes to all of my classes. I have this problem for many of them.

700z
  • 1
  • 2
  • 2
    Difficult to tell what you mean without seeing some code, ideally a MVCE – stuartd May 09 '18 at 14:34
  • 3
    Please post a [mcve]. – Lasse V. Karlsen May 09 '18 at 14:35
  • The problem is not Json.Net - it is you violating SRP. The class should only be used for de-/serialization. – Sir Rufo May 09 '18 at 14:53
  • You have tagged this [tag:json.net] but Json.NET does not have a type `JsonTextSerializer`. May I instead assume use of the [`JsonConvert`](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConvert.htm) class? If so, I made the following [mcve] for reference: https://dotnetfiddle.net/No0QHV – dbc May 09 '18 at 23:16
  • Looks like `CollectionClearingContractResolver` from [Populate object where objects are reused and arrays are replaced?](https://stackoverflow.com/a/42167029/3744182) solves your problem. See https://dotnetfiddle.net/VIzl72 for a working fiddle. Unless you have some additional problem I'll go ahead and close this as a duplicate. – dbc May 09 '18 at 23:23
  • Actually, setting `ObjectCreationHandling = ObjectCreationHandling.Replace` in `JsonSerializerSettings` works also. See https://dotnetfiddle.net/AgRHNc and [Repeated serialization and deserialization creates duplicate items](https://stackoverflow.com/q/24835262/3744182). So this could be a duplicate of both questions. – dbc May 09 '18 at 23:46
  • 1
    @dbc it looks like the `CollectionClearingContractResolver` solves it, and it's not too slow. Thanks! – 700z May 10 '18 at 10:17

1 Answers1

1

You can use the [JsonProperty] attribute with the DefaultValueHandling property.

Given this class:

public class Foo
{
    [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    public string Bar { get; set; }
    public List<bool> Baz { get; set; }

    public Foo()
    {
        Bar = "Not Null";
        Baz = new List<bool>
        {
            false
        };
    }
}

And this JSON:

{ "baz" : [ true ] }

This code:

string json = "{ \"baz\" : [ true ] }";

var foo = JsonConvert.DeserializeObject<Foo>(json, new JsonSerializerSettings
{
    ObjectCreationHandling = ObjectCreationHandling.Replace
});

Console.WriteLine(foo.Bar ?? "(null)");
foreach (var b in foo.Baz)
{
    Console.WriteLine(b);
}

Will print:

(null)
True

Meaning the Bar string will be overwritten with the value from the JSON, being null since it's missing.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • I don't want to add that to all of my classes. I have this problem for many of them – 700z May 09 '18 at 17:12