我正在尝试反序列化派生类型,我想使用自定义属性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
}
}
我目前正在处理的一个项目遇到了一个非常相似的问题:我想创建一个自定义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;
}
从Base
类中删除[JsonConverter(typeof(TypeSerializer))]
属性,并在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());
}
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 类。
我遇到了与此类似的问题,遇到了无限循环。
我正在使用的 API 可能会返回错误响应或预期类型。我解决了这个问题的方式与其他人使用序列化程序所强调的相同。填充。
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>();
}
为什么会出现此循环?我认为我们假设了一个谬误。我最初尝试呼叫基地。ReadJson 认为我正在覆盖现有功能,而实际上有很多 JsonConverter,我们的自定义转换器并没有覆盖任何东西,因为基类没有真正的方法。最好将基类视为接口。发生循环的原因是我们注册的转换器是引擎认为最适用于要转换的类型的转换器。除非我们可以在运行时从转换器列表中删除我们自己的转换器,否则在我们的自定义转换器中调用引擎进行反序列化将创建无限递归。