1

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 Answer 1

3

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:

  1. 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 the Application_Start() method in your global.asax.

    var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
    settings.TypeNameHandling = TypeNameHandling.Auto;
    
  2. Alternatively, you can change your Web API method to create and return an HttpResponseMessage with a StringContent object containing your JSON and with the mediaType set to application/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;
    
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Brian. I control both sides of the process and I am using Web API. However, I am not manually serializing the data so I'll need to figure out why the double-serialization is occurring but what you're saying makes sense.
Ok, so I think the issue is related to the fact that I need to use Json.Net's TypeNameHandling settings in order to get the $type information into the serialized json. So yes, I am performing a JsonConvert.SerializeObject, with TypeNameHandling settings, and then return that string. Web API appears to be serializing the json yet again. Is there a way to prevent that from happening? I have't found anything obvious.
I've updated my answer with some more information which should help solve the issue.
Thanks again, Brian. Your approach solves the serialization issue. I still have another issue related to deserialization but it's just something else I am going to have to address. Thanks again for the assistance.
No problem; glad I could help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.