Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clear collections before adding items when populating existing objects

Tags:

json

c#

json.net

I have an object which has several collection properties defined with public getter but private setter, In this case JsonConvert.PopulateObject adds the deserialized items to these collections leaving the existing items untouched.

I need a behavior when such member collections get cleared before deserialization.

I tried to manually clear the collections in a method marked with the [OnDeserializing] attribute.

The problem with that approach is that it will still clear the collections even if the collection property does not exist in the JSON string.

I need a way when only those collections get cleared which are actually defined in the JSON string. Those which are undefined should be kept untouched.

Thanks

like image 759
Zoltán Tamási Avatar asked Feb 18 '16 13:02

Zoltán Tamási


1 Answers

Okay, so after some trip to Json.NET sources I found the following solution by inheriting a custom contract resolver from DefaultContractResolver.

I needed to override the array contract creation to add a deserialization callback. At this point the callback receives the concrete collection instance, so we can manipulate it (in this case clear it).

As long as I can determine, it is safe to use, but feel free to warn about any drawbacks of this method.

Note: Am I the only one who feels that this should probably be the default behavior?

public class CollectionClearingContractResolver : DefaultContractResolver
{
    protected override JsonArrayContract CreateArrayContract(Type objectType)
    {
        var c = base.CreateArrayContract(objectType);
        c.OnDeserializingCallbacks.Add((obj, streamingContext) =>
        {
            var list = obj as IList;
            if (list != null && !list.IsReadOnly)
                list.Clear();
        });
        return c;
    }
}

...

public class Test {
    public List<int> List { get; private set; }
    public Test() {
        List = new List<int>();
    }
}  

...

var myObj = new Test();
myObj.List.AddRange(new[] {1,2,3});
var listReference = myObj.List;    

JsonConvert.PopulateObject("{ List: [4, 5, 6] }", myObj, 
    new JsonSerializerSettings {
        ContractResolver = new CollectionClearingContractResolver(),
    });

myObj.List.ShouldEqual(listReference); // didn't recreate list
myObj.List.Count.ShouldEqual(3);
myObj.List.SequenceEqual(new[] { 4, 5, 6}).ShouldBeTrue();
like image 129
Zoltán Tamási Avatar answered Oct 30 '22 07:10

Zoltán Tamási