如何配置JSON.net反序列化器来跟踪缺失的属性?

样本类:

public class ClassA { public int Id { get; set; } public string SomeString { get; set; } public int? SomeInt { get; set; } } 

默认反序列化器:

 var myObject = JsonConvert.DeserializeObject(str); 

为两个不同的输入创建相同的对象

 {"Id":5} 

要么

 {"Id":5,"SomeString":null,"SomeInt":null} 

如何跟踪反序列化过程中丢失的属性并保留相同的行为? 有没有办法覆盖一些JSON.net序列化方法(例如DefaultContractResolver类方法)来实现这一点。 例如:

 List missingProps; var myObject = JsonConvert.DeserializeObject(str, settings, missingProps); 

对于第一个输入列表,应包含缺少的属性的名称(“SomeString”,“SomeInt”),对于第二个输入,它应该为空。 反序列化的对象保持不变。

在反序列化期间查找空/未定义标记的另一种方法是编写自定义JsonConverter ,以下是自定义转换器的示例,它可以报告省略的标记(例如"{ 'Id':5 }" )和空标记(例如{"Id":5,"SomeString":null,"SomeInt":null}

 public class NullReportConverter : JsonConverter { private readonly List _nullproperties=new List(); public bool ReportDefinedNullTokens { get; set; } public IEnumerable NullProperties { get { return _nullproperties; } } public void Clear() { _nullproperties.Clear(); } public override bool CanConvert(Type objectType) { return true; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { existingValue = existingValue ?? Activator.CreateInstance(objectType, true); var jObject = JObject.Load(reader); var properties = objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var property in properties) { var jToken = jObject[property.Name]; if (jToken == null) { _nullproperties.Add(property); continue; } var value = jToken.ToObject(property.PropertyType); if(ReportDefinedNullTokens && value ==null) _nullproperties.Add(property); property.SetValue(existingValue, value, null); } return existingValue; } //NOTE: we can omit writer part if we only want to use the converter for deserializing public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var objectType = value.GetType(); var properties = objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); writer.WriteStartObject(); foreach (var property in properties) { var propertyValue = property.GetValue(value, null); writer.WritePropertyName(property.Name); serializer.Serialize(writer, propertyValue); } writer.WriteEndObject(); } } 

注意:如果我们不需要使用它来序列化对象,我们可以省略Writer部分。

用法示例:

 class Foo { public int Id { get; set; } public string SomeString { get; set; } public int? SomeInt { get; set; } } class Program { static void Main(string[] args) { var nullConverter=new NullReportConverter(); Console.WriteLine("Pass 1"); var obj0 = JsonConvert.DeserializeObject("{\"Id\":5, \"Id\":5}", nullConverter); foreach(var p in nullConverter.NullProperties) Console.WriteLine(p); nullConverter.Clear(); Console.WriteLine("Pass2"); var obj1 = JsonConvert.DeserializeObject("{\"Id\":5,\"SomeString\":null,\"SomeInt\":null}" , nullConverter); foreach (var p in nullConverter.NullProperties) Console.WriteLine(p); nullConverter.Clear(); nullConverter.ReportDefinedNullTokens = true; Console.WriteLine("Pass3"); var obj2 = JsonConvert.DeserializeObject("{\"Id\":5,\"SomeString\":null,\"SomeInt\":null}", nullConverter); foreach (var p in nullConverter.NullProperties) Console.WriteLine(p); } } 

1. JSON有一个你class级缺少的属性

使用属性JsonSerializerSettings.MissingMemberHandling可以说明缺少的属性是否被视为错误。

您可以安装错误委托,它将注册错误。

这将检测JSON字符串中是否存在某些“垃圾”属性。

 public class ClassA { public int Id { get; set; } public string SomeString { get; set; } } internal class Program { private static void Main(string[] args) { const string str = "{'Id':5, 'FooBar': 42 }"; var myObject = JsonConvert.DeserializeObject(str , new JsonSerializerSettings { Error = OnError, MissingMemberHandling = MissingMemberHandling.Error }); Console.ReadKey(); } private static void OnError(object sender, ErrorEventArgs args) { Console.WriteLine(args.ErrorContext.Error.Message); args.ErrorContext.Handled = true; } } 

你的class级有一个JSON缺少的财产

选项1:

使它成为必需的财产:

  public class ClassB { public int Id { get; set; } [JsonProperty(Required = Required.Always)] public string SomeString { get; set; } } 

选项2:

使用一些“特殊”值作为默认值,然后检查。

 public class ClassB { public int Id { get; set; } [DefaultValue("NOTSET")] public string SomeString { get; set; } public int? SomeInt { get; set; } } internal class Program { private static void Main(string[] args) { const string str = "{ 'Id':5 }"; var myObject = JsonConvert.DeserializeObject(str , new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); if (myObject.SomeString == "NOTSET") { Console.WriteLine("no value provided for property SomeString"); } Console.ReadKey(); } } 

选项3:

另一个好主意是将这个检查封装在类本身之外。 创建一个Verify()方法,如下所示,并在反序列化后调用它。

 public class ClassC { public int Id { get; set; } [DefaultValue("NOTSET")] public string SomeString { get; set; } public int? SomeInt { get; set; } public void Verify() { if (SomeInt == null ) throw new JsonSerializationException("SomeInt not set!"); if (SomeString == "NOTSET") throw new JsonSerializationException("SomeString not set!"); } } 

我遇到了这个问题,但由于POCO对象,defaultValue不是解决方案。 我认为这比NullReportConverter更简单。 有三个unit testing。 Root是封装整个json的类。 键是属性的类型。 希望这有助于某人。

 using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; namespace SomeNamespace { [TestClass] public class NullParseJsonTest { [TestMethod] public void TestMethod1() { string slice = "{Key:{guid:\"asdf\"}}"; var result = JsonConvert.DeserializeObject(slice); Assert.IsTrue(result.OptionalKey.IsSet); Assert.IsNotNull(result.OptionalKey.Value); Assert.AreEqual("asdf", result.OptionalKey.Value.Guid); } [TestMethod] public void TestMethod2() { string slice = "{Key:null}"; var result = JsonConvert.DeserializeObject(slice); Assert.IsTrue(result.OptionalKey.IsSet); Assert.IsNull(result.OptionalKey.Value); } [TestMethod] public void TestMethod3() { string slice = "{}"; var result = JsonConvert.DeserializeObject(slice); Assert.IsFalse(result.OptionalKey.IsSet); Assert.IsNull(result.OptionalKey.Value); } } class Root { public Key Key { get { return OptionalKey.Value; } set { OptionalKey.Value = value; OptionalKey.IsSet = true; // This does the trick, it is never called by JSON.NET if attribute missing } } [JsonIgnore] public Optional OptionalKey = new Optional { IsSet = false }; }; class Key { public string Guid { get; set; } } class Optional { public T Value { get; set; } public bool IsSet { get; set; } } }