我正在使用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
}
答案 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;
}
}