Json.Net复杂对象的反序列化-包含基类作为属性的基类 [英] Json.Net Deserialization of Complex Objects - Base Class that Contains a Base Class as a Property
问题描述
几个小时以来,我一直在尝试解决此问题.我尝试了许多方法并阅读了许多文章.我似乎找不到解决方法.
我有一个对象层次结构,由此我具有包含另一个派生类型的派生类型.我正在使用Json.Net的TypeNameHandling
设置.在序列化和反序列化方面,我都将其设置为Auto
.
生成的JSON是:
"[\r\n {\r\n \"$type\": \"Common.Monitors.HeartbeatMonitor, Common\",\r\n \"ClassName\": \"HeartbeatMonitor\",\r\n \"ServerGroupMonitorId\": 1,\r\n \"Instructions\": {\r\n \"$type\": \"Common.Monitors.HeartbeatMonitorInstructions, Common\",\r\n \"RunIntervalInMinutes\": 1\r\n },\r\n \"LastExecutionDateTime\": \"2016-03-22T16:35:18.7458519\"\r\n },\r\n {\r\n \"$type\": \"Common.Monitors.DiskSpaceMonitor, Common\",\r\n \"ClassName\": \"DiskSpaceMonitor\",\r\n \"ServerGroupMonitorId\": 2,\r\n \"Instructions\": {\r\n \"$type\": \"Common.Monitors.DiskSpaceMonitorInstructions, Common\",\r\n \"DriveLetter\": \"C:\\\",\r\n \"AlertWhenPercentFreeLessThan\": 20,\r\n \"RunIntervalInMinutes\": 30\r\n },\r\n \"LastExecutionDateTime\": \"2016-03-22T16:15:18.7458519\"\r\n }\r\n]"
尝试使用以下方法反序列化时:
string json = response.Content.ReadAsStringAsync().Result;
IEnumerable<MonitorBase> monitors = JsonConvert.DeserializeObject<IEnumerable<MonitorBase>>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
我收到以下异常:
Newtonsoft.Json.dll中发生了'Newtonsoft.Json.JsonSerializationException'类型的异常,但未在用户代码中处理.
其他信息:转换值时出错
"[ { "$type": "Common.Monitors.HeartbeatMonitor, Common", "ClassName": "HeartbeatMonitor", "ServerGroupMonitorId": 1, "Instructions": { "$type": "Common.Monitors.HeartbeatMonitorInstructions, Common", "RunIntervalInMinutes": 1 }, "LastExecutionDateTime": "2016-03-22T16:35:18.7458519" }, { "$type": "Common.Monitors.DiskSpaceMonitor, Common", "ClassName": "DiskSpaceMonitor", "ServerGroupMonitorId": 2, "Instructions": { "$type": "Common.Monitors.DiskSpaceMonitorInstructions, Common", "DriveLetter": "C:\\", "AlertWhenPercentFreeLessThan": 20, "RunIntervalInMinutes": 30 }, "LastExecutionDateTime": "2016-03-22T16:15:18.7458519" } ]"
键入"System.Collections.Generic.IEnumerable`1 [Common.Monitors.MonitorBase]". 路径",第1行,位置943.
HeartbeatMonitor
和DiskSpaceMonitor
类型是从MonitorBase
派生的,它们各自的Instructions
类型是从MonitorInstructionBase
派生的.
我忍不住想起我遗漏了一些明显的东西,因为我无法想象这不是一个很常见的情况.
我认为问题可能在于您的JSON是双重序列化的,如转义引号\"
,可见的换行符\r\n
和引用整个JSON的事实.反序列化双序列化JSON时,结果是字符串,而不是对象,该字符串显然不能转换为任何类型的IEnumerable<T>
.因此是错误.
如果您控制JSON的来源,请确保仅序列化对象一次.造成此问题的一个常见原因是未意识到某些框架(例如Web API)会自动为您进行序列化,因此,如果在返回对象之前先手动序列化对象,那么最终将得到双序列化JSON. /p>
如果您不控制JSON的来源,和/或无法让所有者修复它,则可以在客户端通过对JSON进行反序列化两次来对其进行更正:首先获取真实的" JSON字符串,然后第二个从中产生您的对象.例如:
string escapedJson = response.Content.ReadAsStringAsync().Result;
string realJson = JsonConvert.DeserializeObject<string>(escapedJson);
IEnumerable<MonitorBase> monitors =
JsonConvert.DeserializeObject<IEnumerable<MonitorBase>>(realJson,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
编辑
我从您的评论中看到,您正在手动序列化对象以利用Json.Net的TypeNameHandling设置,并且您正在寻找一种避免双重序列化的方法.
您基本上有两个选择:
-
将
TypeNameHandling
设置添加到Web API的全局序列化设置中,然后从您的方法中删除手动序列化代码.为此,请在global.asax
中的Application_Start()
方法中添加以下内容.var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings; settings.TypeNameHandling = TypeNameHandling.Auto;
-
或者,您可以更改Web API方法以创建并返回带有包含JSON的
StringContent
对象并将mediaType
设置为application/json
的HttpResponseMessage
.这样可以防止Web API尝试重新序列化结果.string json = JsonConvert.SerializeObject(monitors, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }); HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(json, Encoding.UTF8, "application/json"); return response;
I have been attempting to resolve this issue for several hours. I have tried many approaches and read many posts. I can't seem to find a resolution.
I have an object hierarchy whereby I have derived types that contain another derived type. I am using Json.Net's TypeNameHandling
setting. I have it set to Auto
on both serialization and deserialization.
The resulting JSON is:
"[\r\n {\r\n \"$type\": \"Common.Monitors.HeartbeatMonitor, Common\",\r\n \"ClassName\": \"HeartbeatMonitor\",\r\n \"ServerGroupMonitorId\": 1,\r\n \"Instructions\": {\r\n \"$type\": \"Common.Monitors.HeartbeatMonitorInstructions, Common\",\r\n \"RunIntervalInMinutes\": 1\r\n },\r\n \"LastExecutionDateTime\": \"2016-03-22T16:35:18.7458519\"\r\n },\r\n {\r\n \"$type\": \"Common.Monitors.DiskSpaceMonitor, Common\",\r\n \"ClassName\": \"DiskSpaceMonitor\",\r\n \"ServerGroupMonitorId\": 2,\r\n \"Instructions\": {\r\n \"$type\": \"Common.Monitors.DiskSpaceMonitorInstructions, Common\",\r\n \"DriveLetter\": \"C:\\\",\r\n \"AlertWhenPercentFreeLessThan\": 20,\r\n \"RunIntervalInMinutes\": 30\r\n },\r\n \"LastExecutionDateTime\": \"2016-03-22T16:15:18.7458519\"\r\n }\r\n]"
When attempting to deserialize using the following:
string json = response.Content.ReadAsStringAsync().Result;
IEnumerable<MonitorBase> monitors = JsonConvert.DeserializeObject<IEnumerable<MonitorBase>>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
I receive the following exception:
An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code.
Additional information: Error converting value
"[ { "$type": "Common.Monitors.HeartbeatMonitor, Common", "ClassName": "HeartbeatMonitor", "ServerGroupMonitorId": 1, "Instructions": { "$type": "Common.Monitors.HeartbeatMonitorInstructions, Common", "RunIntervalInMinutes": 1 }, "LastExecutionDateTime": "2016-03-22T16:35:18.7458519" }, { "$type": "Common.Monitors.DiskSpaceMonitor, Common", "ClassName": "DiskSpaceMonitor", "ServerGroupMonitorId": 2, "Instructions": { "$type": "Common.Monitors.DiskSpaceMonitorInstructions, Common", "DriveLetter": "C:\\", "AlertWhenPercentFreeLessThan": 20, "RunIntervalInMinutes": 30 }, "LastExecutionDateTime": "2016-03-22T16:15:18.7458519" } ]"
to type 'System.Collections.Generic.IEnumerable`1[Common.Monitors.MonitorBase]'. Path '', line 1, position 943.
The HeartbeatMonitor
and DiskSpaceMonitor
types derive from MonitorBase
and their respective Instructions
types derive from MonitorInstructionBase
.
I can't help but assume that I am missing something obvious as I can't imagine that this isn't a rather common scenario.
I think that the problem might be that your JSON is double-serialized, as evidenced by the escaped quote marks \"
, the visible newlines \r\n
and the fact that the whole JSON is quoted. When you deserialize double-serialized JSON, the result is a string, not an object, which clearly cannot be converted to an IEnumerable<T>
of any kind. Hence the error.
If you control the source of the JSON, be sure that you are serializing your objects only once. A common cause of the problem is in not realizing that some frameworks, such as Web API, do serialization automatically for you, so if you are serializing your objects manually first before returning them then you'll end up with double-serialized JSON.
If you do not control the source of the JSON, and/or you cannot get the owner to fix it, then you can correct for it on the client side by deserializing the JSON twice: first to get the "real" JSON string, then second to produce your objects from that. For example:
string escapedJson = response.Content.ReadAsStringAsync().Result;
string realJson = JsonConvert.DeserializeObject<string>(escapedJson);
IEnumerable<MonitorBase> monitors =
JsonConvert.DeserializeObject<IEnumerable<MonitorBase>>(realJson,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
EDIT
I see from your comment that you are manually serializing your object in order to make use of Json.Net's TypeNameHandling setting, and you're looking for a way to avoid the double serialization.
You basically have two options:
Add the
TypeNameHandling
setting to Web API's global serialization settings and remove the manual serialization code from your method. To do this, add the following in theApplication_Start()
method in yourglobal.asax
.var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings; settings.TypeNameHandling = TypeNameHandling.Auto;
Alternatively, you can change your Web API method to create and return an
HttpResponseMessage
with aStringContent
object containing your JSON and with themediaType
set toapplication/json
. This should prevent Web API from trying to re-serialize the result.string json = JsonConvert.SerializeObject(monitors, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }); HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(json, Encoding.UTF8, "application/json"); return response;
这篇关于Json.Net复杂对象的反序列化-包含基类作为属性的基类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!