我正在尝试使用带有自定义JsonConverter
的JSON.NET序列化可空结构。我想在JSON输出中忽略/忽略null
值,例如我希望下面的JSON输出为{}
,而不是{"Number":null}
。如何做到这一点?这是带有我要实现的单元测试的最小复制。
[Fact]
public void UnitTest()
{
int? number = null;
var json = JsonConvert.SerializeObject(
new Entity { Number = new HasValue<int?>(number) },
new JsonSerializerSettings()
{
DefaultValueHandling = DefaultValueHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
});
Assert.Equal("{}", json); // Fails because json = {"Number":null}
}
public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?>? Number { get; set; }
}
public struct HasValue<T>
{
public HasValue(T value) => this.Value = value;
public object Value { get; set; }
}
public class NullJsonConverter : JsonConverter
{
public override bool CanRead => false;
public override bool CanConvert(Type objectType) => true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var values = (HasValue<int?>)value;
var objectValue = values.Value;
if (objectValue == null)
{
// How can I skip writing this property?
}
else
{
var token = JToken.FromObject(objectValue, serializer);
token.WriteTo(writer);
}
}
}
答案 0 :(得分:3)
您在这里遇到三个问题:
如this answer至 Custom Json.NET converter should not serialize a property 所述:
custom
JsonConverter
不能阻止其值被序列化,因为引用该属性的属性名称将在调用转换器时被写出。在Json.NET的体系结构中,包含类型的责任是确定要序列化其属性的哪个。然后,值转换器决定如何对要写入的值进行序列化。
third
不起作用,因为属性NullValueHandling.Ignore
不为空,它具有一个值,即带有{的已分配Entity.Number
结构{1}} 内在价值:
HasValue<int?>
类似地,null
不起作用,因为Number = new HasValue<int?>(number) // Not Number = null
与可为空的可空值具有相同的值-如上所述,它与分配给DefaultValueHandling.Ignore
的值不同。
那么您在这里有什么选择?
当default(HasValue<int?>?)
的值为非空但内部值为空的情况下,可以使用 conditional property serialization 禁止序列化Number
:
Number
演示小提琴#1 here。
但是,这种设计似乎有些复杂-您有一个可空值,其中包含一个封装了包含整数的可空值的结构-即public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?>? Number { get; set; }
public bool ShouldSerializeNumber() { return Number.HasValue && Number.Value.Value.HasValue; }
}
。您真的需要两个级别的可为空吗?如果没有,您只需移除外部Nullable<HasValue<Nullable<int>>>
,Nullable<>
现在将正常工作:
DefaultValueHandling
演示小提琴#2 here。
在两种情况下,我都将public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?> Number { get; set; }
}
概括为NullJsonConverter
来处理所有可能的类型T
,如下所示:
HasValue<T>
具体由:
public struct HasValue<T> : IHasValue
{
// Had to convert to c# 4.0 syntax for dotnetfiddle
T m_value;
public HasValue(T value) { this.m_value = value; }
public T Value { get { return m_value; } set { m_value = value; } }
public object GetValue() { return Value; }
}
internal interface IHasValue
{
object GetValue();
}
public class NullJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType) { throw new NotImplementedException(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var valueType = objectType.GetGenericArguments()[0];
var valueValue = serializer.Deserialize(reader, valueType);
return Activator.CreateInstance(objectType, valueValue);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, ((IHasValue)value).GetValue());
}
}
属性。因此,您可以根据需要将Value
应用于[JsonConverter(typeof(NullJsonConverter))]
本身。
出于 Why are mutable structs “evil”? 中所述的原因,您可能还考虑将HasValue<T>
结构设为不可变。