问题
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.
回答1:
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;
来源:https://stackoverflow.com/questions/36184162/json-net-deserialization-of-complex-objects-base-class-that-contains-a-base-cl