我有一个看起来像这样的自定义类:
public class PartnerLoginOptions
{
public string Username { get; set; }
public string Password { get; set; }
public string DeviceModel { get; set; }
public string Version { get; set; }
public bool? IncludeUrls { get; set; }
public bool? ReturnDeviceType { get; set; }
public bool? ReturnUpdatePromptVersions { get; set; }
}
我想忽略序列化时具有默认值的任何bool?
成员,但要保持带有空值的字符串。例如,如果我有这样的对象
var options = new PartnerLoginOptions
{
Username = null,
Password = "123",
IncludeUrls = null,
ReturnDeviceType = true
};
然后序列化将导致这一点:
{
"username": null,
"password": "123",
"deviceModel": null,
"version": null,
"returnDeviceType": true
}
这是我到目前为止的代码:
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
DefaultValueHandling = DefaultValueHandling.Ignore // this applies to strings too, not just bool?
};
return JsonConvert.SerializeObject(options, settings);
有什么方法可以在不单独使用[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
标记每个OptionalBool
属性的情况下进行此操作?谢谢。
您可以创建一个从DefaultContractResolver
继承的自定义合同解析器,该解析器将DefaultValueHandling.Ignore
应用于所需类型的所有属性。但是,您的特定问题有一些约束:
-
您想将默认值处理应用于系统类型,即
bool?
。因此,不能通过将某些自定义属性应用于该类型,然后在CreateProperty()
中检查合同解决方案。相反,有必要将一系列覆盖物传递给合同解析器的构造函数。 -
您正在使用
CamelCasePropertyNamesContractResolver
。如果您将其子类并将不同的替代列表传递给不同的实例,那么您将遇到这个问题的错误,即在每个子类型的所有实例中都强行共享合同,即使不同的实例否则会返回不同的合同。因此,有必要从没有此错误的DefaultContractResolver
继承,然后将DefaultContractResolver.NamingStrategy
设置为CamelCaseNamingStrategy
。
因此,您的合同解析器应该看起来像:
public class DefaultValueContractResolver : DefaultContractResolver
{
readonly Dictionary<Type, DefaultValueHandling> overrides;
public DefaultValueContractResolver(IEnumerable<KeyValuePair<Type, DefaultValueHandling>> overrides) : base()
{
if (overrides == null)
throw new ArgumentNullException("overrides");
this.overrides = overrides.ToDictionary(p => p.Key, p => p.Value);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.DefaultValueHandling == null)
{
DefaultValueHandling handling;
if (overrides.TryGetValue(property.PropertyType, out handling))
property.DefaultValueHandling = handling;
}
return property;
}
}
然后使用它:
var options = new PartnerLoginOptions
{
Username = null,
Password = "123",
IncludeUrls = null,
ReturnDeviceType = true
};
var resolver = new DefaultValueContractResolver(
new Dictionary<Type, DefaultValueHandling>
{
{ typeof(bool?), DefaultValueHandling.Ignore }
})
{
NamingStrategy = new CamelCaseNamingStrategy()
};
var settings = new JsonSerializerSettings { ContractResolver = resolver };
var json = JsonConvert.SerializeObject(options, Formatting.Indented, settings);
注意:
如果您有一个标准的类型列表,您始终应用默认值处理覆盖,则应缓存合同解析器的实例以获得最佳性能。
JSON.NET 9.0.1中引入了合同解析器命名策略。如果您使用的是较早版本并且需要骆驼套,则需要子类
DefaultValueContractResolver
并自己添加,例如如下:public class DefaultValueCamelCaseContractResolver : DefaultValueContractResolver { public DefaultValueCamelCaseContractResolver(IEnumerable<KeyValuePair<Type, DefaultValueHandling>> overrides) : base(overrides) { } protected override string ResolvePropertyName(string propertyName) { return propertyName.ToCamelCase(); } } public static class CamelCaseNameExtensions { class CamelCaseNameResolver : CamelCasePropertyNamesContractResolver { // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit static CamelCaseNameResolver() { } internal static readonly CamelCaseNameResolver Instance = new CamelCaseNameResolver(); // Purely to make the protected method public. public string ToCamelCase(string propertyName) { return ResolvePropertyName(propertyName); } } public static string ToCamelCase(this string propertyName) { if (propertyName == null) return null; return CamelCaseNameResolver.Instance.ToCamelCase(propertyName); } }
演示小提琴。