Muh*_*eed 3 .net c# json json.net jsonconverter
我正在尝试使用带有自定义JsonConverter. 我希望null在 JSON 输出中忽略/省略一个值,例如我希望下面的 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
你在这里有三个问题:
如自定义 Json.NET 转换器的答案中所述,不应序列化属性:
一个自定义
JsonConverter不能阻止它的价值被序列化,因为属性名称指的是将已经被转换器调用的时间写出来。在 Json.NET 的体系结构中,由包含类型决定要序列化它的哪些属性;然后值转换器决定如何序列化正在写入的值。
NullValueHandling.Ignore不工作,因为财产Entity.Number是不为空,它有一个值,即分配的HasValue<int?>结构具有null 内在价值:
Number = new HasValue<int?>(number) // Not Number = null
Run Code Online (Sandbox Code Playgroud)同样DefaultValueHandling.Ignore不起作用,因为default(HasValue<int?>?)它与可空的 null 值具有相同的值——如上所述,它与分配给 的值不同Number。
那么你有什么选择呢?
您可以使用条件属性序列化来抑制Number其值为非空但内部值为空的序列化:
public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?>? Number { get; set; }
public bool ShouldSerializeNumber() { return Number.HasValue && Number.Value.Value.HasValue; }
}
Run Code Online (Sandbox Code Playgroud)
演示小提琴 #1在这里。
但是,这种设计似乎有点过于复杂——您有一个包含一个结构的可空对象,该结构封装了一个包含整数的可空对象——即Nullable<HasValue<Nullable<int>>>. 你真的需要两个级别的可空值吗?如果没有,您可以简单地删除外部Nullable<>,DefaultValueHandling现在就可以工作了:
public class Entity
{
[JsonConverter(typeof(NullJsonConverter))]
public HasValue<int?> Number { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
演示小提琴 #2在这里。
在这两种情况下,我全身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());
}
}
Run Code Online (Sandbox Code Playgroud)
具体通过:
Value要键入的属性。因此[JsonConverter(typeof(NullJsonConverter))],HasValue<T>如果您愿意,您可以自行申请。
您可能还考虑使您的HasValue<T>结构不可变,原因在为什么可变结构是“邪恶的”?.