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();
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…