示例类:
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”),对于第二个输入,它应该为空。反序列化的对象保持不变。
答案 0 :(得分:8)
使用属性JsonSerializerSettings.MissingMemberHandling ,您可以说缺少的属性是否被视为错误。
您可以安装将注册错误的Error委托。
这将检测是否有一些&#34;垃圾&#34; 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;
}
}
使其成为必需的财产:
public class ClassB
{
public int Id { get; set; }
[JsonProperty(Required = Required.Always)]
public string SomeString { get; set; }
}
选项2:
使用一些&#34;特殊&#34;将值作为默认值并在之后进行检查。
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!");
}
}
答案 1 :(得分:5)
在反序列化期间查找空/未定义标记的另一种方法是编写自定义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);
}
}
答案 2 :(得分:2)
我遇到了这个问题,但由于POCO对象,defaultValue不是解决方案。我认为这比NullReportConverter更简单。 有三个单元测试。 Root是封装整个json的类。 Key是Property的类型。希望这有助于某人。
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; }
}
}