如何将属性应用于继承树中的单个类型?

时间:2014-01-27 00:08:55

标签: c# json inheritance attributes json.net

这个问题主要针对我的具体问题,但我觉得这里有一个更大的问题需要解决。

使用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());
    }
}

问题但是,BikeCar 继承属性,告诉序列化器使用哪个转换器。

结果是JSON.NET将永远停留在ReadJson循环中,导致StackOverflowException(如何合适)。

所以问题是:如何以一种子类不继承它的方式将属性应用于抽象类?

4 个答案:

答案 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;
}

到目前为止,在我的简单测试中它似乎工作正常。