在JSON.NET序列化空 [英] Serializing null in JSON.NET
问题描述
在通过序列化JSON.NET,那就是空写入JSON作为
When serializing arbitrary data via JSON.NET, any property that is null is written to the JSON as
参数propertyName:空
"propertyName" : null
这当然是正确的。
不过,我有一个要求,自动把所有空到默认为空值,例如:空字符串
取值应成为的String.Empty
,空诠释?
取值应成为 0
,空布尔?
s应该是假
,系统等。
However I have a requirement to automatically translate all nulls into the default empty value, e.g. null string
s should become String.Empty
, null int?
s should become 0
, null bool?
s should be false
, and so on.
NullValueHandling
是没有帮助的,因为我不想忽略
为空,但我也不希望包含
他们(嗯,新的功能?)。
NullValueHandling
is not helpful, since I dont want to Ignore
nulls, but neither do I want to Include
them (Hmm, new feature?).
于是我转过身来实现自定义的 JsonConverter
。
虽然实现本身是一件轻而易举的事,可惜这仍然没有工作 - CanConvert()
是从来没有要求,有一个空值,因此属性 WriteJson()
不叫任。显然,空值会自动直接序列化为空
,没有自定义管道。
So I turned to implementing a custom JsonConverter
.
While the implementation itself was a breeze, unfortunately this still didnt work - CanConvert()
is never called for a property that has a null value, and therefore WriteJson()
is not called either. Apparently nulls are automatically serialized directly into null
, without the custom pipeline.
例如,这里是一个自定义转换为空字符串的例子:
For example, here is a sample of a custom converter for null strings:
public class StringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(string).IsAssignableFrom(objectType);
}
...
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
string strValue = value as string;
if (strValue == null)
{
writer.WriteValue(String.Empty);
}
else
{
writer.WriteValue(strValue);
}
}
}
通过这个在调试器步进,我指出,无论这些方法都要求有一个空值的属性。
Stepping through this in the debugger, I noted that neither of these methods are called for properties that have a null value.
钻研JSON.NET的源$ C $ C,我发现(显然,我没有去成很多深度)有一个特殊的情况下检查空值和显式调用 .WriteNull( )
。
Delving into JSON.NET's sourcecode, I found that (apparently, I didnt go into a lot of depth) there is a special case checking for nulls, and explictly calling .WriteNull()
.
有关它的价值,我也尝试实现自定义的 JsonTextWriter
和覆盖默认 .WriteNull()
实施......
For what it's worth, I did try implementing a custom JsonTextWriter
and overriding the default .WriteNull()
implementation...
public class NullJsonWriter : JsonTextWriter
{
...
public override void WriteNull()
{
this.WriteValue(String.Empty);
}
}
不过,这并不能很好地工作,因为 WriteNull()
方法一无所知的基本数据类型。那么肯定,我可以输出,
任何空,但是,这并不很好的工作,例如INT,BOOL等。
However, this can't work well, since the WriteNull()
method knows nothing about the underlying datatype. So sure, I can output ""
for any null, but that doesnt work well for e.g. int, bool, etc.
所以,我的问题 - 短手动将整个数据结构,是对此有任何解决方案或解决方法吗?
So, my question - short of converting the entire data structure manually, is there any solution or workaround for this?
推荐答案
好吧,我想我已经想出了一个解决方案,(我的第一个解决方案是不正确的,在所有的,但后来我又是在火车上)。你需要创建一个特殊的合同解析器和一个自定义ValueProvider为可空类型。试想一下:
Okay, I think I've come up with a solution (my first solution wasn't right at all, but then again I was on the train). You need to create a special contract resolver and a custom ValueProvider for Nullable types. Consider this:
public class NullableValueProvider : IValueProvider
{
private readonly object _defaultValue;
private readonly IValueProvider _underlyingValueProvider;
public NullableValueProvider(MemberInfo memberInfo, Type underlyingType)
{
_underlyingValueProvider = new DynamicValueProvider(memberInfo);
_defaultValue = Activator.CreateInstance(underlyingType);
}
public void SetValue(object target, object value)
{
_underlyingValueProvider.SetValue(target, value);
}
public object GetValue(object target)
{
return _underlyingValueProvider.GetValue(target) ?? _defaultValue;
}
}
public class SpecialContractResolver : DefaultContractResolver
{
protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
{
if(member.MemberType == MemberTypes.Property)
{
var pi = (PropertyInfo) member;
if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof (Nullable<>))
{
return new NullableValueProvider(member, pi.PropertyType.GetGenericArguments().First());
}
}
else if(member.MemberType == MemberTypes.Field)
{
var fi = (FieldInfo) member;
if(fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
return new NullableValueProvider(member, fi.FieldType.GetGenericArguments().First());
}
return base.CreateMemberValueProvider(member);
}
}
然后,我测试了它使用:
Then I tested it using:
class Foo
{
public int? Int { get; set; }
public bool? Boolean { get; set; }
public int? IntField;
}
和下面的案例:
[TestFixture]
public class Tests
{
[Test]
public void Test()
{
var foo = new Foo();
var settings = new JsonSerializerSettings { ContractResolver = new SpecialContractResolver() };
Assert.AreEqual(
JsonConvert.SerializeObject(foo, Formatting.None, settings),
"{\"IntField\":0,\"Int\":0,\"Boolean\":false}");
}
}
希望这有助于有点...
Hopefully this helps a bit...
编辑&ndash的;更好地确定了一个可空&LT;&GT;
键入的
编辑&ndash的;增加了对域的支持,以及性能,在上方还背负正常 DynamicValueProvider
做的大部分工作,以更新的测试的
Edit – Added support for fields as well as properties, also piggy-backing on top of the normal DynamicValueProvider
to do most of the work, with updated test
这篇关于在JSON.NET序列化空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!