I am building a C#/.NET 4.5 client for a REST API using JSON.NET. The API supports partial updates; therefore the presence or lack of an attribute in the json on an update has meaning. If the attribute is in the json, the server will set the value accordingly; the the attribute is not passed the server will not update it. This also applies to null values. I have .NET classes for each model; with properties for each JSON attribute (pretty standard).
As an example lets say I have this account object (name, notes) that already exists on the server:
{
'name':'craig',
'notes:'these are notes'
}
If I pass in this json for an update it will update the name, but will leave the notes set to 'these are notes':
var account = api.GetAccount();
account.Name = "bob";
api.UpdateAccount(account);
{
'name':'bob'
}
If I pass this json in for an update, it will set the name and the notes to null on the server:
var account = api.GetAccount();
account.Name = "bob";
account.Notes = null;
api.UpdateAccount(account);
{
'name':'bob',
'notes':null
}
All good up to this point.
My question is how to you get JSON.NET to play along nicely with this. JSON.NET allows control the NullValueHandling which basically says if null values should be serialized or not. However that is not enough in this case. I need to be able to determine if the calling code explicitly set the value to null. Is there a recommended way to handle this?
Ive tried using a Dictionary internal to my models to store the attributes to be serialized via JSON. This allows me to tell if the attribute has been set to anything (including null) via the presence of the key in the dictionary. I found that this approach has some difficulties and I end up rewriting a lot of code that comes standard to JSON.NET (type serialization, generics, nullables, enums...).
Note: I do realize the above example is a bit contrived. In reality the account object returned back from the server would have both name and notes populated, and that when the update happened it would send both back.
The other case where this applies is during creating objects and handling server generated default. For example, lets say the server defaults the account's notes to 'put notes here' when the account is created. If I pass in the Notes attribute with a null value, the server will think the client wants to set it to null. The reality though is the client is not trying to set the Notes to null, and in this case would want the default to be set.
var account = new Account();
account.Name = "bob";
api.CreateAccount(account);
{
'name':'bob',
'notes':null
}
See Question&Answers more detail:
os