JSON.NET:反序列化类包含从接口派生的对象列表

时间:2013-03-14 11:56:22

标签: c# json.net deserialization

我很难解除对象的反序列化。它包含以下属性:

public List<IShape> Shapes { get; set; };

和JSON.NET反序列化器总是告诉我,实例化接口是不可能的。

我有几个类实现了实现IShape接口的接口,例如Polyline -> IPolyline -> IShape。我已经尝试了两种解决方案:

但我得到了同样的例外,即IShape无法立即播出,被抛出。

我使用TypeNameHandling.Auto序列化对象,TypeNameHandling.All也没有帮助,即使我使用上面链接的帖子中提到的转换器。

有谁知道这个问题的解决方案?如果需要一些代码,我很乐意发布它。

以下是生成的JSON示例。

"$type": "SketchModel.Layer, SketchModel",
        "Id": 57865477,
        "Shapes": {
          "$type": "System.Collections.Generic.List`1[[SketchModel.Shapes.AbstractShapes.IShape, SketchModel]], mscorlib",
          "$values": [
            {
              "$type": "SketchModel.Shapes.Polyline, SketchModel",

此行负责解决问题:

"System.Collections.Generic.List`1[[SketchModel.Shapes.AbstractShapes.IShape, SketchModel]], mscorlib"

它根本不知道如何实例化IShape。如果我创建一个自定义转换器并让它返回每个IShape的折线,它可以工作,但不会创建任何其他形状(例如椭圆)。

在 在自定义转换器中覆盖的public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)方法,如果我让我打印objectType的完整类型名称,它总是IShape,从来没有其他任何东西......

3 个答案:

答案 0 :(得分:2)

异常是有道理的,因为反序列化器不知道界面在保湿时应该表示的具体类型。

在序列化期间,JSON.NET允许您将其配置为添加一些要在这种情况下使用的元数据。 This SO question有一个答案解释如何配置它。

配置将向反序列化期间将使用的JSON添加类型属性。

答案 1 :(得分:1)

我确实遇到了这个问题,只是通过明确为Type提供转换器来解决它。如果我使用注释它不起作用 - 我需要在反序列化时传递转换器 - 基本上是这样的:

state = JsonConvert.DeserializeObject<StateImpl>((String)stateObject, new JsonConverter[] { new StatePersistenceStateEntryConverter(), new StatePersistenceUserInteractionConverter() });

我的StateImpl对象包含这些属性:

    [DataMember]
    [JsonProperty("stateEntries", TypeNameHandling = TypeNameHandling.Auto)]
    public List<IStateEntry> StateEntries
    {
        get;
        set;
    }

    [DataMember]
    [JsonProperty("precommitStateEntry", TypeNameHandling = TypeNameHandling.Auto)]
    public IPrecommitStateEntry PrecommitStateEntry
    {
        get;
        set;
    }

IPrecommitStateEntry扩展了IStateEntry接口(如果您想知道为什么转换器中有额外的逻辑,那么就是FYI。)

另外 - 在我的IStateEntry对象中,我有类似的孩子问题:

    [DataMember]
    [JsonProperty("userInteractions", TypeNameHandling = TypeNameHandling.Auto)]
    public List<IUserInteraction> UserInteractions
    {
        get;
        set;
    }

因此我的对象具有IStateEntry的子列表属性,并且IStateEntry具有IUserInteraction的另一个子列表。我的转换器如下:

public class StatePersistenceStateEntryConverter : CustomCreationConverter<IStateEntry>
{
    public override IStateEntry Create(Type objectType)
    {
        if (objectType == typeof(IPrecommitStateEntry))
        {
            return new PrecommitStateEntry();
        }
        else
        {
            return new StateEntry();
        }
    }
}

和...

public class StatePersistenceUserInteractionConverter : CustomCreationConverter<IUserInteraction>
{
    public override IUserInteraction Create(Type objectType)
    {
        return new UserInteraction();
    }
}

他们所做的一切就是创建特定对象实现的实例。

所以我不知道为什么需要转换器,因为可以清楚地实例化List - 而不是单独的IStateEntry。显然,NewtonSoft实现中存在一个错误 - 或者我缺少一些基本的东西。

希望有所帮助。对我来说这是一个令人沮丧的几个小时,但现在正在工作!

答案 2 :(得分:0)

在Json字符串中不包含具体类型,您可以使用JsonConverterIList<SomeInterface>转换为具体类型:

要反序列化的类:

public partial class MovieInfo : IMovieInfo
{
    ~~~~

    [JsonProperty("genres")]
    [JsonConverter(typeof(ListConverter<IGenre, Genre>))]
    public IList<IGenre> Genres { get; set; }

    ~~~~
}

JsonConverter示例:

public class ListConverter<I, T> : JsonConverter
{
    public override bool CanWrite => false;
    public override bool CanRead => true;
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(I);
    }
    public override void WriteJson(JsonWriter writer,
         object value, JsonSerializer serializer)
    {
        throw new InvalidOperationException("Use default serialization.");
    }

    public override object ReadJson(JsonReader reader,
         Type objectType, object existingValue,
         JsonSerializer serializer)
    {
        JArray jsonArray = JArray.Load(reader);
        var deserialized = (List<T>)Activator.CreateInstance(typeof(List<T>));
        serializer.Populate(jsonArray.CreateReader(), deserialized);
        return deserialized as IList<I>;
    }
}