我想出的解决方案。我在使用 JsonConverter 时遇到了很多问题,所以我选择使用 ContractResolver。似乎这通常只是我们目前如何使用 API 的限制。实际上有很多关于这个的讨论。真的没有办法区分它的默认值和已经设置的东西。我只是选择创建一些字符串填充值来指示用户已在 JSON 中发送了该值。这使 JSON 与传递时发送的完全相同。但是,映射的结果完全不同,开发人员可以看出现在实际上还没有设置。
不幸的是,我不得不提出 2 个合约解析器,因为它们本身并不处理对象的 instance,而且我对当前实例的条件序列化感兴趣。我最终不得不实例化我的自定义合同解析器并传递我感兴趣的对象的实例。
如果有人知道如何更好地做到这一点,请告诉我!总的来说,我花了很多时间浏览 GitHub 并将不同的堆栈溢出帖子拼凑在一起。
代码:
解串器
// PreInstalled Packages
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
// From NuGet - Default
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class CrudDeserializer: DefaultContractResolver
{
// Designated respresentation for a null value passed through JSON, its default is "JsonNull"
private string NullRepresentation;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return type.GetProperties()
.Select(p=>{
var jp = base.CreateProperty(p, memberSerialization);
jp.ValueProvider = new NullToUniqueStringValueProvider(p, this.NullRepresentation);
return jp;
}).ToList();
}
public CrudDeserializer(string nullRepresentation = "JsonNull")
{
if(nullRepresentation == null)
{
throw new Exception ("nullRepresentation cannot be NULL. It kind of defeats the purpose");
}
this.NullRepresentation = nullRepresentation;
}
}
// Second class
public class NullToUniqueStringValueProvider : IValueProvider
{
private PropertyInfo MemberInfo;
private string NullRepresentation;
public NullToUniqueStringValueProvider(PropertyInfo memberInfo, string nullRepresentation)
{
this.MemberInfo = memberInfo;
this.NullRepresentation = nullRepresentation;
}
public object GetValue(object target)
{
throw new Exception("This class is not used for serialization");
}
public void SetValue(object target, object value)
{
if ((string)value == null)
{
MemberInfo.SetValue(target, this.NullRepresentation);
}
else
{
MemberInfo.SetValue(target, value);
}
}
}
序列化器
// PreInstalled Packages
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
// From NuGet - Default
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class CrudSerializer<T>: DefaultContractResolver
{
private string NullRepresentation;
private T InstantiatedObject;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return type.GetProperties()
.Select(p=>{
var jp = this.CreateProperty(p, memberSerialization);
jp.ValueProvider = new UniqueStringToNull(p, this.NullRepresentation);
return jp;
}).ToList();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
PropertyInfo pi = member as PropertyInfo;
string value = (string)pi.GetValue(this.InstantiatedObject);
if (value == null)
{
property.ShouldSerialize =
instance =>
{
return false;
};
}
return property;
}
public CrudSerializer(T someInstance, string nullRepresentation = "JsonNull")
{
if(nullRepresentation == null)
{
throw new Exception ("nullRepresentation cannot be NULL. It kind of defeats the purpose");
}
this.NullRepresentation = nullRepresentation;
this.InstantiatedObject = someInstance;
}
}
public class UniqueStringToNull : IValueProvider
{
private PropertyInfo MemberInfo;
private string NullRepresentation;
public UniqueStringToNull(PropertyInfo memberInfo, string nullRepresentation)
{
this.MemberInfo = memberInfo;
this.NullRepresentation = nullRepresentation;
}
public object GetValue(object target)
{
object result = MemberInfo.GetValue(target);
if (MemberInfo.PropertyType == typeof(string) && (string)result == this.NullRepresentation)
{
result = null;
}
return result;
}
public void SetValue(object target, object value)
{
throw new Exception ("This ContractResolver cannot be used for deserialization");
}
}
例子:
public class SomeClass
{
public string RequiredProperty {get;set;}
public string RequiredProperty2 {get;set;}
public string OptionalProperty3 {get;set;}
public string OptionalProperty4 {get;set;}
public SomeClass(){}
}
class Program
{
static void Main(string[] args)
{
// Example JSON payloads
string JsonExample1 = @"
{""RequiredProperty"":""search"",""RequiredProperty2"":""this"",""OptionalProperty3"": null}";
string JsonExample2 = @"
{""RequiredProperty"":""search"",""RequiredProperty2"":""this""}";
JsonSerializerSettings deserializationSettings = new JsonSerializerSettings {
ContractResolver = new CrudDeserializer()
};
// Deserializing/Serializing JsonExample1
SomeClass sc1 = JsonConvert.DeserializeObject<SomeClass>(JsonExample1, deserializationSettings );
// Passing over current instance of object to ContractResolver
JsonSerializerSettings serializationSettings1 = new JsonSerializerSettings {
ContractResolver = new CrudSerializer<SomeClass>(sc1)
};
string json1 = JsonConvert.SerializeObject(sc1, Formatting.None, serializationSettings1);
// Deserializing/Serializing JsonExample2
SomeClass sc2 = JsonConvert.DeserializeObject<SomeClass>(JsonExample2, deserializationSettings);
// Passing over current instance of object to ContractResolver
JsonSerializerSettings serializationSettings2 = new JsonSerializerSettings {
ContractResolver = new CrudSerializer<SomeClass>(sc2)
};
string json2 = JsonConvert.SerializeObject(sc2, Formatting.None, serializationSettings2);
// Done, JSON is the exact same no matter how many times I deserialize.
// However, in the background, I am able to tell now if a NULL value was sent!
}
}