Клиент API для клиента с частичным обновлением JSON.NET

Я создаю клиент C # /. NET 4.5 для REST API с помощью JSON.NET. API поддерживает частичные обновления; поэтому наличие или отсутствие атрибута в json для обновления имеет смысл. Если атрибут находится в json, сервер будет устанавливать значение соответствующим образом; атрибут не передается, сервер не будет его обновлять. Это также относится к нулевым значениям. У меня есть classы .NET для каждой модели; со свойствами для каждого атрибута JSON (довольно стандартный).

В качестве примера можно сказать, что у меня есть этот объект учетной записи (имя, заметки), который уже существует на сервере:

{ 'name':'craig', 'notes:'these are notes' } 

Если я перейду в этом json для обновления, он обновит имя, но оставит заметки в виде «это заметки»:

 var account = api.GetAccount(); account.Name = "bob"; api.UpdateAccount(account); { 'name':'bob' } 

Если я передам этот json для обновления, он установит имя и примечания на нуль на сервере:

 var account = api.GetAccount(); account.Name = "bob"; account.Notes = null; api.UpdateAccount(account); { 'name':'bob', 'notes':null } 

Все хорошо до этого момента.

Мой вопрос заключается в том, как вы получаете JSON.NET, чтобы хорошо играть с этим. JSON.NET позволяет управлять NullValueHandling, который в основном говорит, если нулевые значения должны быть сериализованы или нет. Однако этого недостаточно. Мне нужно определить, действительно ли вызывающий код задал значение null. Есть ли рекомендуемый способ справиться с этим?

Ive попытался использовать словарь, встроенный в мои модели, для хранения атрибутов, которые будут сериализованы через JSON. Это позволяет мне определить, был ли атрибут настроен на что угодно (включая нуль) через наличие ключа в словаре. Я обнаружил, что этот подход имеет некоторые трудности, и я в конечном итоге переписываю много кода, который поставляется стандартным для JSON.NET (тип сериализации, generics, nullables, enums …).

Примечание. Я понимаю, что приведенный выше пример немного надуман. На самом деле объект учетной записи, возвращенный с сервера, будет иметь как имя, так и ноты, и что, когда произойдет обновление, он отправит оба обратно.

Другой случай, когда это применимо, – это создание объектов и обработка сервера, сгенерированного по умолчанию. Например, предположим, что сервер по умолчанию отмечает заметки учетной записи, чтобы «создавать заметки здесь», когда создается учетная запись. Если я передам атрибут Notes с нулевым значением, сервер будет думать, что клиент хочет установить его в null. Реальность, однако, заключается в том, что клиент не пытается установить Notes в значение null, и в этом случае требуется, чтобы значение по умолчанию было установлено.

 var account = new Account(); account.Name = "bob"; api.CreateAccount(account); { 'name':'bob', 'notes':null } 

Я всегда впечатлен JSON.NET …

Вот что я закончил. Я использовал комбинацию ContractResolver, предиката ShouldSerialize и свойства NullValueHandling. Эта ссылка была очень полезна. Свойства хранятся в словаре в базовом classе ApiModel; этот код прост.

Модель счета

 [JsonProperty("name")] public string Name { get { return this.GetAttributeValue("name"); } set { this.SetAttributeValue("name", value); } } 

Json Serialization

 ApiModel.JsonSerializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); ApiModel.JsonSerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include; ApiModel.JsonSerializerSettings.ContractResolver = ApiModel.JsonContractResolver; internal class ApiModelContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver { protected override Newtonsoft.Json.Serialization.JsonProperty CreateProperty(System.Reflection.MemberInfo member, Newtonsoft.Json.MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); property.ShouldSerialize = instance => { var apiModel = instance as ApiModel; var hasAttribute = apiModel.HasAttribute(property.PropertyName); property.NullValueHandling = hasAttribute ? Newtonsoft.Json.NullValueHandling.Include : Newtonsoft.Json.NullValueHandling.Ignore; return hasAttribute; }; return property; } }