JSON.NET部分更新Rest API客户端

时间:2014-03-23 20:13:17

标签: c# .net json rest json.net

我正在使用JSON.NET为REST API构建C#/ .NET 4.5客户端。 API支持部分更新;因此,更新中json中存在或缺少属性具有意义。如果属性在json中,则服务器将相应地设置该值;该属性未传递服务器将不会更新它。这也适用于空值。我为每个模型都有.NET类;具有每个JSON属性的属性(非常标准)。

举一个例子,假设我有一个已存在于服务器上的帐户对象(名称,备注):

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

如果我传入此json进行更新,它将更新名称,但会将注释设置为'这些是注释':

var account = api.GetAccount();
account.Name = "bob";
api.UpdateAccount(account);

{
   'name':'bob'
}

如果我将此json传递给更新,它会在服务器上将名称和注释设置为null:

var account = api.GetAccount();
account.Name = "bob";
account.Notes = null;
api.UpdateAccount(account);

{
   'name':'bob',
   'notes':null
}

到目前为止一切都很好。

我的问题是如何让JSON.NET与它一起玩得很好。 JSON.NET允许控制NullValueHandling,它基本上表示是否应该序列化null值。然而,在这种情况下,这还不够。我需要能够确定调用代码是否显式将值设置为null。有没有推荐的方法来处理这个?

我尝试使用模型内部的Dictionary来存储要通过JSON序列化的属性。这允许我通过字典中的键的存在来判断属性是否已被设置为任何(包括null)。我发现这种方法有一些困难,我最终重写了许多JSON.NET标准的代码(类型序列化,泛型,nullables,枚举......)。

注意:我确实意识到上面的例子有点做作。实际上,从服务器返回的帐户对象将填充名称和备注,并且当更新发生时,它将向后发送。

适用的另一种情况是在创建对象和处理服务器生成的默认值期间。例如,假设服务器默认帐户的注释为“在此处放置注释”。创建帐户时如果我传入带有null值的Notes属性,服务器会认为客户端想要将其设置为null。但实际情况是客户端没有尝试将Notes设置为null,在这种情况下需要设置默认值。

var account = new Account();
account.Name = "bob";
api.CreateAccount(account);

{
   'name':'bob',
   'notes':null
}

1 个答案:

答案 0 :(得分:1)

我总是对JSON.NET印象深刻...

这是我最终的结果。我使用了ContractResolver,ShouldSerialize谓词和NullValueHandling属性的组合。这个link非常有用。属性存储在基类ApiModel中的Dictionary中;该代码很简单。

帐户模型

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

Json序列化

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;
    }
}