4

我在反序列化我的对象时遇到了麻烦。它包含以下属性:

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,而不是其他任何东西......

4

3 回答 3

2

这个例外是有道理的,因为反序列化器不知道在水合时接口应该代表什么具体类型。

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

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

于 2013-03-14T12:02:26.850 回答
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 接口(仅供参考,以防您想知道为什么转换器中有额外的逻辑)。

另外 - 在我的 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 实现中的某个地方存在错误 - 或者我错过了一些基本的东西。

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

于 2013-11-08T08:13:10.487 回答
0

在您的 Json 字符串中不包含具体类型,您可以使用 aJsonConverter将 an 转换IList<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>;
    }
}
于 2018-05-05T18:38:16.297 回答