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



示例类:

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

默认反序列化器:

var myObject = JsonConvert.DeserializeObject<ClassA>(str);

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

{"Id":5}

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

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

List<string> missingProps;
var myObject = JsonConvert.DeserializeObject<ClassA>(str, settings, missingProps);

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

JSON有一个属性在你的类中缺失

使用属性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<ClassA>(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;
    }
}

2。你的类有一个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<ClassB>(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!");
    }
}

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

public class NullReportConverter : JsonConverter
{
    private readonly List<PropertyInfo> _nullproperties=new List<PropertyInfo>();
    public bool ReportDefinedNullTokens { get; set; }
    public IEnumerable<PropertyInfo> 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<Foo>("{"Id":5, "Id":5}", nullConverter);
        foreach(var p in nullConverter.NullProperties)
            Console.WriteLine(p);
        nullConverter.Clear();
        Console.WriteLine("Pass2");
        var obj1 = JsonConvert.DeserializeObject<Foo>("{"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<Foo>("{"Id":5,"SomeString":null,"SomeInt":null}", nullConverter);
        foreach (var p in nullConverter.NullProperties)
            Console.WriteLine(p);
    }
}

我得到了这个问题,但由于POCO对象defaultValue不是解决方案。我认为这是比NullReportConverter更简单的方法。有三个单元测试。根是封装整个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<Root>(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<Root>(slice);
      Assert.IsTrue(result.OptionalKey.IsSet);
      Assert.IsNull(result.OptionalKey.Value);
    }
    [TestMethod]
    public void TestMethod3()
    {
      string slice = "{}";
      var result = JsonConvert.DeserializeObject<Root>(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<Key> OptionalKey = new Optional<Key> { IsSet = false };
  };

  class Key {
    public string Guid { get; set; }
  }
  class Optional<T> {
    public T Value { get; set; }
    public bool IsSet { get; set; }
  }
}

相关内容

  • 没有找到相关文章

最新更新