当我遇到一个有副作用的属性设置器时,出现了一个问题(对Separation of concerns来说这是不好的设计)。
以下问题是理论上的问题,从理论上讲,应该通过消除属性设置器的副作用来解决此问题。
但是如果不能修改DTO类,是否有任何机制可以防止使用Json.NET时JSON反序列化引起副作用?
问题场景:
下面显示的是Myclass
和两个JSON代码段。
调用JsonConvert.Deserialize<MyClass>(json)
时,根据JSON属性的顺序,会得到不同的结果。
数据传输类别:
public class MyClass
{
private string _evilField1;
public string EvilField1
{
get
{
return _evilField1;
}
set
{
_evilField1 = value;
Info = "EvilField1.Set messed Info";
}
}
private string _evilField2;
public string EvilField2
{
get
{
return _evilField2;
}
set
{
_evilField2 = value;
Info = "EvilField2.Set messed Info";
}
}
public string Info { get; set; }
}
输入数据
{
"EvilField1": "Foo",
"EvilField2": "Foo",
"Info": "Initial value"
}
{
"EvilField1": "Foo",
"Info": "Initial value",
"EvilField2": "Foo"
}
反序列化数据
第一个JSON不会触发副作用,但是第二个JSON会带来讨厌的输出:
{
"EvilField1": "Foo",
"Info": "Initial value",
"EvilField2": "Foo"
}
{
"EvilField1": "Foo",
"EvilField2": "Foo",
"Info": "EvilField2.Set messed Info"
}
答案 0 :(得分:3)
您可以为有问题的类创建自定义JsonConverter
。这样一来,您就可以以所需的任何方式填充它,以解决有害的副作用。
public class MyClassConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MyClass);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
JObject obj = JObject.Load(reader);
// Populate the "Info" field last so it will not be overwritten
return new MyClass
{
EvilField1 = (string)obj["EvilField1"],
EvilField2 = (string)obj["EvilField2"],
Info = (string)obj["Info"]
};
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
要使用转换器,您可以添加有问题的类的[JsonConverter]
属性...
[JsonConverter(typeof(MyClassConverter))]
public class MyClass
{
...
}
...,也可以通过设置将转换器的实例传递给DerserializeObject()
方法:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new MyClassConverter());
var obj = JsonConvert.DeserializeObject<MyClass>(json, settings);