这个问题主要针对我的具体问题,但我觉得这里有一个更大的问题需要解决。
使用JSON.NET库,我正在尝试使用继承树将复杂的JSON类型反序列化为.NET对象模型。 因为在编译时无法确定文档的类型,所以我编写了自己的转换器,根据JSON属性值确定类型。
为了简短起见,我的对象模型的结构类似于以下内容:
[JsonConverter(typeof(JsonVehicleConverter))]
abstract class Vehicle
{
public int Wheels { get; set; }
}
class Bike : Vehicle
{
}
class Car : Vehicle
{
public int Doors { get; set; }
}
看看上面的模型,我想我可以像这样反序列化我的文档:
string json = @"{
""type"": ""car"",
""wheels"": 4,
""doors"": 2
}";
Vehicle vehicle = JsonConvert.DeserializeObject<Vehicle>(json);
我的自定义转换器将继续并解析该值,然后决定使用哪个实现:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.ReadFrom(reader);
var type = jsonObject.Value<string>("type");
if (type == "bike")
{
return serializer.Deserialize<Bike>(jsonObject.CreateReader());
}
else if (type == "car")
{
return serializer.Deserialize<Car>(jsonObject.CreateReader());
}
}
问题但是,Bike
和Car
继承属性,告诉序列化器使用哪个转换器。
结果是JSON.NET将永远停留在ReadJson
循环中,导致StackOverflowException
(如何合适)。
所以问题是:如何以一种子类不继承它的方式将属性应用于抽象类?
答案 0 :(得分:1)
您是否尝试过将属性更改为不继承?
[System.AttributeUsage(System.AttributeTargets.All,
AllowMultiple = false,
Inherited = false)]
public JsonVehicleConverterAttribute : System.Attribute
{
...
}
答案 1 :(得分:1)
解决此问题的一种方法是不在类上放置属性,而是将转换器的实例传递给DeserializeObject
方法。
Vehicle vehicle = JsonConvert.DeserializeObject<Vehicle>(json,
new JsonVehicleConverter);
然后,在转换器的ReadJson
方法中,不要使用传递给方法的序列化程序来创建对象实例。而是使用ToObject<T>
:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.ReadFrom(reader);
var type = jsonObject.Value<string>("type");
if (type == "bike")
{
return jsonObject.ToObject<Bike>();
}
else if (type == "car")
{
return jsonObject.ToObject<Car>();
}
throw new JsonSerializationException();
}
最后,请确保CanConvert
方法已正确实施(如果尚未执行):
public override bool CanConvert(Type objectType)
{
return typeof(Vehicle).IsAssignableFrom(objectType);
}
答案 2 :(得分:0)
从概念上讲,您希望继承它,但它只是在抽象类中的变量,并成为子类的不变量。您可以这样做以强制正确初始化不变量:
abstract class Vehicle
{
protected string _type;
public int Wheels { get; set; }
public Vehicle(string type)
{
this._type = type;
}
}
class Bike : Vehicle
{
public Vehicle()
: base("Bike")
{
// no implementation
}
}
class Car : Vehicle
{
public int Doors { get; set; }
public Vehicle()
: base("Car")
{
// no implementation
}
}
答案 3 :(得分:0)
我设法使用类似的方法在我的转换器类上使用ThreadLocal静态变量。这种方法有点不对劲,但似乎确实有效。
在我的转换器类中,我有一个静态ThreadLocal变量
private static readonly ThreadLocal<int> nesting = new ThreadLocal<int>();
在我的CanRead属性中,我测试了这个
public override bool CanRead { get { return nesting.Value == 0; } }
我在我的ReadJson方法中增加它
nesting.Value = nesting.Value + 1;
try {
....
}
finally {
nesting.Value = nesting.Value - 1;
}
到目前为止,在我的简单测试中它似乎工作正常。