反序列化后无法转换对象

时间:2014-05-03 22:22:20

标签: c# json

我在Newtonsoft.Json中序列化然后反序列化Dictionary,但是输出不是我预期的并且抛出异常。有人能让我知道我做错了吗?

这是我的代码

using Newtonsoft.Json;

namespace Task1
{
    public class Class1
    {
        public static void Main()
        {
            var dict = new Dictionary<string, object>();
            dict["int"] = new GenericItem<int> {CreatedAt = DateTime.Now, Object = 111};
            dict["string"] = new GenericItem<string> {CreatedAt = DateTime.Now.Date, Object = "test test"};

            var json = JsonConvert.SerializeObject(dict);
            var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

            var test = dict["int"] is GenericItem<int>;
            var test2 = dict["int"] as GenericItem<int>;
            var test3 = (GenericItem<int>) dict["int"];

            var desTest = desDict["int"] is GenericItem<int>;
            var desTest2 = desDict["int"] as GenericItem<int>;
            var desTest3 = (GenericItem<int>) desDict["int"];
        }

    }

    public class GenericItem<T>
    {
        public DateTime CreatedAt { get; set; }
        public T Object { get; set; }
    }
}

前三个测试返回TrueGenericItem<Int>的实例和GenericItem<Int>的实例。

但反序列化后返回falsenullInvalidCastException

有什么问题?为什么在deserealization之后抛出InvalidCastException

3 个答案:

答案 0 :(得分:3)

  

有什么问题?为什么在deserealization之后抛出InvalidCastException

这种行为的原因很简单 - 如果您使用desDict["int"].GetType()检查此元素的类型,您会看到它返回Newtonsoft.Json.Linq.JObject并且这绝对不是您期望的类型

根据this SO answer中的建议,可以使用TypeNameHandling类的JsonSerializerSettings参数执行您想要的操作。改变这个:

var json = JsonConvert.SerializeObject(dict);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

到此:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
var json = JsonConvert.SerializeObject(dict, settings);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);

来自参考答案的信息:

  

TypeNameHandling标志将向JSON添加$ type属性   允许Json.NET知道反序列化所需的具体类型   把对象变成了。这允许您在静止时反序列化对象   实现接口或抽象基类。

     

然而,缺点是这是特定于Json.NET的。该   $ type将是一个完全限定的类型,因此如果您要将其序列化   类型信息,反序列化器需要能够理解它   好。

作为替代方案,您可以循环反序列化字典,将每个键的值从JObject转换为所需类型:

var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var newDict = new Dictionary<string, object>(); //as you can't modify collection in foreach loop and the original dictionary can be out of scope of deserialization code

foreach (var key in desDict.Keys)
{ 
    switch(key)
    {
        case "int":
            newDict[key] = ((JObject)desDict[key]).ToObject<GenericItem<int>>();
            break;
        case "string":
            newDict[key] = ((JObject)desDict[key]).ToObject<GenericItem<string>>();
            break;
    }
}

desDict = newDict;

这里你不需要那个手动解决方案,但也许有一天这些知识会很有用。我在this SO answer中找到了有关JOobject.ToObject()方法的信息。我已经检查了这两种方法,并且两者都可以正常使用您提供的代码。

答案 1 :(得分:1)

你要求它反序列化为Dictionary<string, object>,所以它会这样做。它不存储类型信息,因此它构建了一个对象来存储反序列化信息。

由于差异问题,我认为你无法得到你想要的东西。您可以反序列化为Dictionary<string, GenericItem<object>>,但这似乎并不是您想要的。您将获得一个特定于json.net的对象来代替您的int或字符串。你可以把它转换成你想要的。

答案 2 :(得分:-3)

尝试:

var desDict = JsonConvert.DeserializeObject<Dictionary<string, GenericItem<object>>>(json);

或:

var desDict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);

使用动态,您根本不需要投射对象,只需使用它的属性