经过大量的源代码挖掘,我解决了我的问题。
事实证明,只有在我反序列化包含字符串属性的复杂对象时,cmets 中提出的所有解决方案才有效。
在这种情况下,是的,只需修改合约解析器即可 [1]。
但是,我需要一种在反序列化时将 any 字符串转换为 null 的方法,并且在我的对象只是一个字符串的情况下,以这种方式修改合同将失败,即, p>
public void MyMethod(string jsonSomeInfo)
{
// At this point, jsonSomeInfo is "\"\"",
// an emmpty string.
var deserialized = new JsonSerializer().Deserialize(new StringReader(jsonSomeInfo), typeof(string));
// deserialized = "", event if I used the modified contract resolver [1].
}
发生的情况是,当我们处理复杂对象时,JSON.NET 在内部将 JsonToken.StartObject 的 TokenType 分配给读取器,这将导致反序列化遵循调用 property.ValueProvider.SetValue(target, value); 的特定路径。
但是,如果对象只是一个字符串,则TokenType 将是JsonToken.String,并且路径将不同,并且永远不会调用值提供者。
无论如何,我的解决方案是构建一个自定义转换器来转换具有TokenType == JsonToken.String 的JsonReaders(代码如下)。
解决方案
public class StringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value == null) return null;
string text = reader.Value.ToString();
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
return text;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Not needed because this converter cannot write json");
}
public override bool CanWrite
{
get { return false; }
}
}
[1] 感谢@Raphaël Althaus。
public class NullToEmptyStringResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return type.GetProperties()
.Select(p => {
var jp = base.CreateProperty(p, memberSerialization);
jp.ValueProvider = new EmptyToNullStringValueProvider(p);
return jp;
}).ToList();
}
}
public class EmptyToNullStringValueProvider : IValueProvider
{
PropertyInfo _MemberInfo;
public EmptyToNullStringValueProvider(PropertyInfo memberInfo)
{
_MemberInfo = memberInfo;
}
public object GetValue(object target)
{
object result = _MemberInfo.GetValue(target);
if (_MemberInfo.PropertyType == typeof(string) && result != null && string.IsNullOrWhiteSpace(result.ToString()))
{
result = null;
}
return result;
}
public void SetValue(object target, object value)
{
if (_MemberInfo.PropertyType == typeof(string) && value != null && string.IsNullOrWhiteSpace(value.ToString()))
{
value = null;
}
_MemberInfo.SetValue(target, value);
}
}