C# 如何配置JSON.net反序列化程序以跟踪丢失的属性?

C# 如何配置JSON.net反序列化程序以跟踪丢失的属性?,c#,json.net,C#,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); 或 如何跟踪反序列化过程中丢失的属性并保持相同的行为?是否有方

示例类:

public class ClassA
{
    public int Id { get; set; }
    public string SomeString { get; set; }
    public int? SomeInt { get; set; }
}
默认反序列化程序:

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

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

List<string> missingProps;
var myObject = JsonConvert.DeserializeObject<ClassA>(str, settings, missingProps);
列出丢失的道具;
var myObject=JsonConvert.DeserializeObject(str、settings、missingProps);
对于第一个输入,列表应包含缺少属性的名称(“SomeString”、“SomeInt”),对于第二个输入,列表应为空。反序列化对象保持不变。

1。JSON具有类中缺少的属性 使用属性,您可以说缺少的属性是否作为错误处理

然后您可以安装将注册错误的委托

这将检测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: 使用一些“特殊”值作为默认值,然后进行检查

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();
    }
}

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

公共类NullReportConverter:JsonConverter
{
私有只读列表_nullproperties=new List();
public bool ReportDefinedNullTokens{get;set;}
公共IEnumerable空属性
{
获取{return}nullproperties;}
}
公共空间清除()
{
_nullproperties.Clear();
}
公共覆盖布尔CanConvert(类型objectType)
{
返回true;
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
existingValue=existingValue??Activator.CreateInstance(objectType,true);
var jObject=jObject.Load(读卡器);
var特性=
GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach(属性中的var属性)
{
var jToken=jObject[property.Name];
如果(jToken==null)
{
_添加(属性);
继续;
}
var值=jToken.ToObject(property.PropertyType);
if(ReportDefinedNullTokens&&value==null)
_添加(属性);
SetValue(existingValue,value,null);
}
返回现有值;
}
//注意:如果我们只想使用转换器进行反序列化,我们可以省略writer部分
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
var objectType=value.GetType();
var特性=
GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
writer.WriteStartObject();
foreach(属性中的var属性)
{
var propertyValue=property.GetValue(值,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);
    }
}
class-Foo
{
公共int Id{get;set;}
公共字符串SomeString{get;set;}
公共int?SomeInt{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var nullConverter=新的NullReportConverter();
控制台写入线(“通过1”);
var obj0=JsonConvert.DeserializeObject(“{\'Id\':5,\'Id\':5}”,nullConverter);
foreach(nullConverter.NullProperties中的var p)
控制台写入线(p);
nullConverter.Clear();
控制台写入线(“Pass2”);
var obj1=JsonConvert.DeserializeObject(“{\'Id\':5,\'SomeString\':null,\'SomeInt\':null}”,nullConverter);
foreach(nullConverter.NullProperties中的var p)
控制台写入线(p);
nullConverter.Clear();
nullConverter.ReportDefinedNullTokens=true;
控制台写入线(“Pass3”);
var obj2=JsonConvert.DeserializeObject(“{\'Id\':5,\'SomeString\':null,\'SomeInt\':null}”,nullConverter);
foreach(nullConverter.NullProperties中的var p)
控制台写入线(p);
}
}

我遇到了这个问题,但由于POCO对象,defaultValue不是解决方案。我认为这是一种比NullReportConverter更简单的方法。 有三个单元测试。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<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; }
  }
}
使用Microsoft.VisualStudio.TestTools.UnitTesting;
使用Newtonsoft.Json;
名称空间名称空间{
[测试类]
公共类NullParseJsonTest{
[测试方法]
公共void TestMethod1()
{
string slice=“{Key:{guid:\“asdf\”}}”;
var result=JsonConvert.DeserializeObject(切片);
Assert.IsTrue(result.OptionalKey.IsSet);
Assert.IsNotNull(result.OptionalKey.Value);
AreEqual(“asdf”,result.OptionalKey.Value.Guid);
}
[测试方法]
公共void TestMethod2()
{
string slice=“{Key:null}”;
var result=JsonConvert.DeserializeObject(切片);
Assert.IsTrue(result.OptionalKey.IsSet);
Assert.IsNull(result.OptionalKey.Value);
}
[测试方法]
公共void TestMethod3()
{
字符串slice=“{}”;
var result=JsonConvert.DeserializeObject(切片);
Assert.IsFalse(result.OptionalKey.IsSet);
Assert.IsNull(result.OptionalKey.Value);
}
}
类根{
公钥{
得到{
返回OptionalKey.Value;
}
设置{
OptionalKey.Value=值;
OptionalKey.IsSet=true;//这就成功了,它永远不会被调用
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();
    }
}
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!");
    }
}
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();
    }
}
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);
    }
}
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; }
  }
}