我在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; }
}
}
前三个测试返回True
,GenericItem<Int>
的实例和GenericItem<Int>
的实例。
但反序列化后返回false
,null
和InvalidCastException
。
有什么问题?为什么在deserealization之后抛出InvalidCastException
?
答案 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);
使用动态,您根本不需要投射对象,只需使用它的属性