反序列化后不能强制转换对象
本文关键字:转换 对象 不能 反序列化 | 更新日期: 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; }
}
}
前三个测试返回True
、GenericItem<Int>
的实例和GenericItem<Int>
的实例。
但是反序列化后返回false
, null
和InvalidCastException
。
有什么问题吗?为什么在反实现之后会抛出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你根本不需要强制转换对象,你只需要使用它的属性