反序列化后不能强制转换对象

本文关键字:转换 对象 不能 反序列化 | 更新日期: 2023-09-27 18:01:26

我在Newtonsoft中序列化然后反序列化一个Dictionary。Json,但是输出不是我所期望的,并且抛出异常。有人能告诉我我哪里做错了吗?

我的代码

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>的实例。

但是反序列化后返回false, nullInvalidCastException

有什么问题吗?为什么在反实现之后会抛出InvalidCastException ?

反序列化后不能强制转换对象

有什么问题吗?为什么在反实现之后会抛出InvalidCastException ?

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

使用JsonSerializerSettings类的TypeNameHandling参数可以做你想做的事情,正如这个SO答案所建议的那样。改变这个:

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将是一个完全限定类型,所以如果你用类型info,,反序列化器需要能够将其理解为好。

作为一种替代方法,您可以遍历反序列化字典,将每个键的值从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;

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

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

我不认为你能得到你想要的,由于差异的问题。您可以反序列化到Dictionary<string, GenericItem<object>>,但这似乎不是您想要的。你会得到一个json.net特定的对象来代替你的int或string。你可以把它变成你想要的。

try:

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

或:

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

使用dynamic你根本不需要强制转换对象,你只需要使用它的属性