17

How can Web API fail to deserialize an object that JSON.Net deserializes?

Visual Studio showing Web API's attempt as all nulls but JSON.Net's properly populated

This is the Web API controller:

public void Put(EditorSubmissionMainView ajaxSubmission) {
// ajaxSubmission: EditorSubmissionMainView with all values ('data' also == null)

    string json = "{\"id\":\"row_1377\",\"data\":{\"ROTATION\":\"1\",\"EQUIPMENT\":[{\"id\":\"6\"},{\"id\":\"8\"}],\"NOTES\":\"\"}}";

    EditorSubmissionMainView foo = Newtonsoft.Json.JsonConvert.DeserializeObject<EditorSubmissionMainView>(json) as EditorSubmissionMainView;
// foo is a EditorSubmissionMainView but properly deserialized.
}

This is the JSON, captured by Fiddler and formatted:

{
    "id": "row_1377",
    "data": {
        "ROTATION": "1",
        "EQUIPMENT": [{
            "id": "6"
        },
        {
            "id": "8"
        }],
        "NOTES": ""
    }
}

An example class that serializes with JSON.Net but not with a Web API controller:

[Serializable]
public class EditorSubmissionMainView
{
    public string id { get; set; }
    public EditorSubmissionMainViewData data { get; set; }
}

[Serializable]
public class EditorSubmissionMainViewData
{
    [JsonProperty("ROTATION")]
    public int? rotation { get; set; } // Same problem if everything is a string

    [JsonProperty("EQUIPMENT")]
    public ICollection<Dictionary<string, int?>> equipment { get; set; }

    [JsonProperty("NOTES")]
    public string notes { get; set; }
}

Web API uses JSON.Net, and I am not using any custom JSON formatters -- just passing the JSON to a Web API controller. Why is this not working?

EDIT: As requested, I am calling my Web API controller with this Javascript (part of JQuery DataTables). Note that I am certain the same JSON is getting to my controller regardless because I've captured the raw HTTP packet with Fiddler and ensured that it is correct:

"ajaxUrl": {
    "create": "POST @Url.Content("~/API/MainView")",
    "edit":   "PUT @Url.Content("~/API/MainView")",
    "remove": "DELETE @Url.Content("~/API/MainView")"
},

"ajax": function (method, url, data, successCallback, errorCallback) {
    $.ajax({
        "type": method,
        "url": url,
        "data": JSON.stringify(data), // Requires IE8+
        "contentType": "application/json",
        "dataType": "json",
        "success": function (json) {
            successCallback(json);
        },
        "error": function (xhr, error, thrown) {
            errorCallback(xhr, error, thrown);
        }
    });
},

The raw HTTP request follows:

PUT http://localhost:53367/API/MainView HTTP/1.1
x-requested-with: XMLHttpRequest
Accept-Language: en-us
Referer: http://localhost:53367/Manage/MainView
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/json
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: localhost:53367
Content-Length: 306
Connection: Keep-Alive
Pragma: no-cache
Cookie: ASP.NET_SessionId=wqsghjrol20cszrxfzdm0qo4

{"id":"row_1377","data":{"ROTATION":"1","EQUIPMENT":[{"id":"6"},{"id":"8"}],"NOTES":""}}
Charles Burns
  • 10,310
  • 7
  • 64
  • 81

2 Answers2

23

Try removing the [Serializable] attributes from your classes EditorSubmissionMainView and EditorSubmissionMainViewData.

Maggie Ying
  • 10,095
  • 2
  • 33
  • 36
  • That fixed the problem. The controller now deserializes the object. WTH? :) Ironically I added `[Serializable]` in a vain attempt to resolve an earlier problem. I still don't understand why JSON.Net can deserialize when used directly because the object is still tagged with `[Serializable]`. Strange! – Charles Burns Oct 11 '12 at 17:00
  • 8
    Json.NET serializer by default set the IgnoreSerializableAttribute to true. In WebAPI, we set that to false. The reason why you hit this issue is because Json.NET ignores properties: "Json.NET now detects types that have the SerializableAttribute and serializes all the fields on that type, both public and private, and ignores the properties" (quoted from http://james.newtonking.com/archive/2012/04/11/json-net-4-5-release-2-serializable-support-and-bug-fixes.aspx) – Maggie Ying Oct 11 '12 at 17:56
  • @Maggie Ying - Any ideas on this question: http://stackoverflow.com/questions/20252027/json-net-web-api-serialization-with-derrived-inherited-types – amcdnl Dec 03 '13 at 16:10
  • 1
    Check my question to make JSON.NET and [Serializable] work together: http://stackoverflow.com/questions/20914080/make-json-net-and-serializable-attribute-work-together/20914389#20914389 – Javier Jan 03 '14 at 23:11
0

I think the problem may be the use of "data" or "id" in your json. Because this has to go through model binder, it may be getting confused by the "id" variable in the default route. Or, it may be getting confused by the generic term "data" that's often used to describe the payload itself.

Try changing these names and let us know if that solves it.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291