我正在尝试反序列化派生类型,我想使用自定义属性Type
来区分派生类型。
[
{
"Type": "a",
"Height": 100
},
{
"Type": "b",
"Name": "Joe"
}
]
我遇到的解决方案是创建自定义JsonConverter
。在ReadJson
上,我读取Type
属性并通过ToObject<T>
函数实例化该类型。一切正常,直到我使用JsonConverterAttribute
。 ReadJson
方法无限循环,因为该属性也应用于子类型。
如何防止将此属性应用于子类型?
[JsonConverter(typeof(TypeSerializer))]
public abstract class Base
{
private readonly string type;
public Base(string type)
{
this.type = type;
}
public string Type { get { return type; } }
}
public class AType : Base
{
private readonly int height;
public AType(int height)
: base("a")
{
this.height = height;
}
public int Height { get { return height; } }
}
public class BType : Base
{
private readonly string name;
public BType(string name)
: base("b")
{
this.name = name;
}
public string Name { get { return name; } }
}
public class TypeSerializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Base);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var j = JObject.Load(reader);
var type = j["Type"].ToObject<string>();
if (type == "a")
// Infinite Loop! StackOverflowException
return j.ToObject<AType>();
if (type == "b")
return j.ToObject<BType>();
throw new NotImplementedException(type);
}
}
[TestFixture]
public class InheritanceSerializeTests
{
[Test]
public void Deserialize()
{
var json = @"{""Type"":""a"", ""Height"":100}";
JObject.Parse(json).ToObject<Base>(); // Crash
}
}
答案 0 :(得分:4)
我当前正在处理的项目遇到了一个非常类似的问题:我想创建一个自定义JsonConverter
并通过属性将其映射到我的实体,但随后代码被困在一个无限循环中。
我的案例中的诀窍是使用serializer.Populate
代替JObject.ToObject
(即使我愿意,也无法使用.ToObject
;我使用的是版本3.5.8 ,其中此功能不存在)。以下是我的ReadJson
方法示例:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JContainer lJContainer = default(JContainer);
if (reader.TokenType == JsonToken.StartObject)
{
lJContainer = JObject.Load(reader);
existingValue = Convert.ChangeType(existingValue, objectType);
existingValue = Activator.CreateInstance(objectType);
serializer.Populate(lJContainer.CreateReader(), existingValue);
}
return existingValue;
}
答案 1 :(得分:3)
从[JsonConverter(typeof(TypeSerializer))]
类中移除Base
属性,在Deserialize
测试中替换以下行:
JObject.Parse(json).ToObject<Base>(); // Crash
这一个:
var obj = JsonConvert.DeserializeObject<Base>(json, new TypeSerializer());
更新1 此更新与问题提问者的评论相符:
将[JsonConverter(typeof(TypeSerializer))]
属性保留为Base
类。使用以下行进行反序列化:
var obj = JsonConvert.DeserializeObject<Base>(json);
并修改ReadJson
方法,如下所示:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var j = JObject.Load(reader);
if (j["Type"].ToString() == "a")
return new AType(int.Parse(j["Height"].ToString()));
return new BType(j["Name"].ToString());
}
答案 2 :(得分:1)
JsonConverters从基类继承。当前没有选择将JsonConverter限制为仅基类。您可以覆盖它。
在Newtonsoft.Json 12.0.3上测试
public class DisabledConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override bool CanRead => false;
public override bool CanWrite => false;
}
然后在派生类上覆盖JsonConverter。
[JsonConverter(typeof(DisabledConverter))]
public class AType : Base
...
[JsonConverter(typeof(DisabledConverter))]
public class BType : Base
...
详细信息
这仅适用于代码:
if (type == "a")
return j.ToObject<AType>();
if (type == "b")
return j.ToObject<BType>();
调用.ToObject时,它将尝试使用基类上定义的转换器(再次)将对象反序列化。这就是造成无限循环的原因。
您需要在派生类上重写JsonConverter。
CanRead => false和CanWrite => false将禁用该类的自定义JsonConverter,强制.ToObject调用在Newtonsoft.Json内部使用默认逻辑,而不是TypeSerializer类。
答案 3 :(得分:0)
我也有类似的问题,并且遇到了无限循环。
我正在使用的Api可能返回错误响应或预期的类型。 我解决问题的方式与其他人使用serializer.Populate突出显示的方式相同。
public class Error{
public string error_code { get; set; }
public string message { get; set; }
}
[JsonConverter(typeof(CustomConverter<Success>))]
public class Success{
public Guid Id { get; set; }
}
public class CustomConverter<T> : JsonConverter where T : new() {
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
if (jObject.ContainsKey("error_code")) {
return jObject.ToObject(typeof(ProvisoErrorResponse));
}
var instance = new T();
serializer.Populate(jObject.CreateReader(), instance);
return instance;
}
}
然后由HttpClient使用,如下所示:
using (var response = await _httpClient.GetAsync(url))
{
return await response.Content.ReadAsAsync<Success>();
}
为什么会发生此循环?我认为我们正在谬误。 我最初尝试调用base.ReadJson认为我是在重写现有功能,而实际上有许多JsonConverters且我们的自定义转换器不重写任何内容,因为基类没有实际方法。最好将基类视为接口。 之所以会发生循环,是因为我们注册的转换器是引擎认为最适合要转换类型的转换器。除非我们可以在运行时从转换器列表中删除自己的转换器,否则在自定义转换器中调用引擎进行反序列化将创建无限递归。