【问题标题】:Json.net deserialization array of interfacesJson.net 反序列化接口数组
【发布时间】:2013-05-08 22:06:15
【问题描述】:

我在 json.net 中收到“类型是接口,无法实例化”的反序列化错误,即使我在尝试反序列化的对象上指定类型

private static JsonSerializerSettings settings = new JsonSerializerSettings { 
    TypeNameHandling = TypeNameHandling.Auto };

/// <summary>
/// Returns an object created from the jObject and placed in a stub object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jObj"></param>
/// <returns></returns>
public static T FromJObject<T>(JToken jObj)
{
    if (jObj == null)
    {
        jObj = new JObject();
    }
    if (jObj is JValue)
    {
        return (T)((JValue)jObj).Value;
    }
    else
    {
        return (T)JsonConvert.DeserializeObject<T>(jObj.ToString(), settings);
    }
 }

这是乔布斯

{
  "Id": 2,
  "Name": "Name",
  "JsonMapEnum": 0,
  "Validations": [
    {
      "Id": 1,
      "$type": "JsonMap.Default.JValidation, JsonMap"
    }
  ],
  "JSType": 3,
  "SubJsonMapEnum": -1,
  "$type": "JsonMap.Default.JAttribute, JsonMap"
}

这是错误

Could not create an instance of type JsonMap.Interfaces.IValidation. Type is an interface or abstract class and cannot be instantated. Path 'Validations[0].Id'

看起来它正在尝试将 Id 转换为 Validation 对象。为什么?

这些是我的类型实现的接口

public interface IJsonMap
{
    long Id { get; set; }
    String Name { get; set; }
    LazyEnum JsonMapEnum { get; set; }
}

public interface IAttribute : IJsonMap
{
    IEnumerable<IValidation> Validations { get; set; }
    LazyEnum JSType { get; set; }
    LazyEnum SubJsonMapEnum { get; set; }
}

public interface IValidation : IJsonMap
{
    IEnumerable<IArgument> Arguments { get; set; }
}

这是电话

FromJObject<JAttribute>(CreationJObj)

JAttribute 实现 IAttribute

【问题讨论】:

    标签: c# json interface polymorphism json.net


    【解决方案1】:

    显然,"$type" 必须是字符串字面量中的第一个属性,才能让类型名称处理程序捕获该类型。我想这是因为反序列化器没有检查 $type 的存在,它只是使用 json 字符串阅读器,并且在找到第一个没有设置类型的属性时,它失败了。

    这是我创建的用于确保 $type 始终是第一位的方法

        private static JToken ReorderJToken(this JToken jTok)
        {
            if (jTok is JArray)
            {
                var jArr = new JArray();
                foreach (var token in jTok as JArray)
                {
                    jArr.Add(token.ReorderJToken());
                }
                return jArr;
            }
            else if( jTok is JObject)
            {
                var jObj = new JObject();
                foreach(var prop in (jTok as JObject).Properties().OrderBy(x=> x.Name))
                {
                    prop.Value = prop.Value.ReorderJToken();
                    jObj.Add(prop);
                }
                return jObj;
            }
            return jTok;
        }
    

    【讨论】:

    • Apparently, "$type" has to be the first property。没必要。假设你是序列化器的作者,你会使用dict["$type"]还是list[0]
    • 很好,但是有一个方法可以重新组织你的属性,出于这个原因,我不得不创建一个实用方法,而且无论如何这并不是一个要求。在处理浏览器和客户端 json 创建时,这似乎不是一个超出左侧字段的用例。
    • 不要误会我的意思,我永远感谢 JamesNK 创造了这个工具,我只想说这是出乎意料的行为,很抱歉让我的挫败感让我看起来很聪明。跨度>
    【解决方案2】:

    很明显,它无法根据您提供的参数推断出正确的类型。如果没有更多关于它的名称的上下文,我无法让你拥有的东西起作用。但是,如果您使用转换为接口类型的对象调用它,它将不起作用。使用 FromObject 的定义必须始终调用 T 是类的类型。首先要做的是确保您永远不要尝试调用T 等于typeof(IOneOfMyInterfaces) 的方法,因为这肯定会失败。如果这不能解决问题,我会尝试调用不同版本的反序列化方法;

    IMyInterface obj = (IMyInterface)serializer.Deserialize(validatingReader, t);
    

    当validatingReader 是JsonReader 并且t 是实现IMyInterface 的类对象的Type 时有效。我目前正在使用它进行通用反序列化,使用 json 模式来验证 json 的正确性。

    【讨论】:

    • 哎呀,对不起,忘了包括那个。 JTransformer.FromJObject(CreationJObj)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    相关资源
    最近更新 更多