Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I've seen some tutorials out there that claim to work, but they are outdated or simply do not work.

How can I use JSON.Net to serialize and deserialize the data received to and sent from my API controllers?

We are using VS2012.

Update

I have a model like this

public class SearchModel
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public Dictionary<string, object> Terms { get; set; }
}

And an Api controller like this

public class ModelSearchApiController : ApiController
{

     public List<Model> Get([FromUri] SearchModel search)
     {
         return new List<Model>();
     }
}

However, search provides the correct value set in the Ajax request, the property Terms is always an empty dictionary.

I know we can provide a value like [ { Key:"foo", Value:123 } ] but why can't I just pass a normal JSON object (ie { foo:123 }) ??? Why can it serialize a Dictionary into a nice standard JSON object, but cannot take that exact same object and recreate a Dictionary. This is beyound me.

Edit

In other words, if the browser sends these arguments :

 pageIndex: 0
 pageSize: 100
 terms[foo]: Bar
 terms[buz]: 1234

What would be the required object signature? Because the object mentionned above does not work and the dictionary is just empty.

share|improve this question
2  
JSON.NET is the default serializer for API controllers (i.e., classes derived from ApiController). You don't need to do anything else, it should just work. Are you having any specific issues? –  carlosfigueira May 14 at 16:13
 
@carlosfigueira, yes. One of my model declare a JsonConverter and the deserializer doesn't use it. This let's me assume that Json.net is not used for deserializing my data. –  Yanick Rochon May 14 at 17:40
 
@ckozl, I am sorry to tell you this, but whatever people say, Json.Net is definitely NOT the default serializer. The default serializer gives an awful non-standard pseudo-JSON response for an object (but still manage to serialize a dictionary right). And I just replaced it with my own. The problem is that it does not read from the request anything. –  Yanick Rochon May 14 at 20:05
 
Again, JSON.NET is the default serializer for API controllers. If you define a JsonConverter, it will be used - for JSON input. See my answer for more complete details. –  carlosfigueira May 14 at 22:07
add comment

2 Answers

up vote 2 down vote accepted

JSON.NET is the default serializer for ASP.NET Web API - it can convert between JSON and CLR objects, and does so for all JSON input. However, you're not trying to convert a JSON input to your SearchModel - you're trying to convert from the URI-based format which is similar to application/x-www-form-urlencoded, into the CLR type SearchModel, and that is not supported by JSON.NET (it's not JSON!). In general, the serializers are used to convert (on incoming requests) from the request body to the action parameter.

Let's look at this (complete) example below (assuming the default route, to "api/{controller}"). It's very similar to your question, but I also added a Post method in addition to the GET method.

public class ModelSearchApiController : ApiController
{
    public List<Model> Get([FromUri] SearchModel search)
    {
        return new List<Model>
        {
            new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
        };
    }

    public List<Model> Post(SearchModel search)
    {
        return new List<Model>
        {
            new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
        };
    }
}

public class Model
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public Dictionary<string, object> Terms { get; set; }
}

public class SearchModel
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public Dictionary<string, object> Terms { get; set; }
}

If you send this request to the server:

POST http://localhost:64699/api/ModelSearchApi HTTP/1.1
User-Agent: Fiddler
Host: localhost:64699
Content-Type: application/json
Content-Length: 65

{"PageIndex":1,"PageSize":10,"Terms":{"foo":"bar","foo2":"bar2"}}

It will be bound, as you expect, to the SearchModel parameter - the Terms property will be a dictionary with two entries (foo=bar, foo2=bar2).

Now, for the GET parameter. ASP.NET Web API has a concept of model binders and value provider, which would be the component which would convert between the query string into the action parameters. The default binder / provider do not support the "arbitrary" name/value pair syntax *for dictionary inside complex types. You can, as you pointed out, use the key/value pair syntax, and that will be understood, as shown below.

GET http://localhost:64699/api/ModelSearchApi?PageIndex=1&PageSize=10&Terms[0][key]=foo&Terms[0][value]=bar HTTP/1.1
User-Agent: Fiddler
Host: localhost:64699

Now, for your problem you have two options. You can change your API to use a custom model binder or value provider which knows how to understand the "simple" name/value syntax, as shown below:

public class ModelSearchApiController : ApiController
{
    public List<Model> Get([ModelBinder(typeof(MySearchModelBinder))] SearchModel search)
    {
        return new List<Model>
        {
            new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
        };
    }
}

public class MySearchModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        SearchModel value = new SearchModel();
        value.Terms = new Dictionary<string,object>();
        foreach (var queryParams in actionContext.Request.GetQueryNameValuePairs())
        {
            if (queryParams.Key == "PageIndex")
            {
                value.PageIndex = int.Parse(queryParams.Value);
            }
            else if (queryParams.Key == "PageSize")
            {
                value.PageSize = int.Parse(queryParams.Value);
            }
            else if (queryParams.Key.StartsWith("Terms."))
            {
                value.Terms.Add(queryParams.Key.Substring("Terms.".Length), queryParams.Value);
            }
        }

        bindingContext.Model = value;
        return true;
    }
}

Another option is to pre-process your input data on the client prior to sending to the server, using a function similar to the one below.

function objToKVPArray(obj) {
    var result = [];
    var k;
    for (k in obj) {
        if (obj.hasOwnProperty(k)) {
            result.push({ key: k, value: obj[k] });
        }
    }
    return result;
}
share|improve this answer
 
I don't know why everyone says that JSON.Net is the default JsonFormatter. For me, I had to replace it because using the default formatter gave invalid JSON notations and did not understand the JSON.Net's class/properties attributes. Anyhow, moving on. I needed to have the same output as the input for my models (what I get from the server should be same that I send back to it), therefore I simply used a custom class extending Dictionary<string, TModel> and having a [JsonArrayAttribute]. This allowed me to have the (ugly and non-standard) array of object {key, value} that C# understand. –  Yanick Rochon May 15 at 1:34
 
If you propose a solution to allow standard JSON objects to be passed as a Dictionary to C# using your value provider, then I'll check your answer as the accepted solution. As for now, this is merely a hack as I don't like this solution (key/value pairs instead of a real JSON object) –  Yanick Rochon May 15 at 1:38
1  
Let's set some terminology, I think we're talking different things. JSON is a wire format. A "JSON object" is something in this format: {"name":<value>}, where <value> can be a string, number, Boolean, null or another JSON object or array. The input that the GET action receives is not a JSON object. It's a translation of a JavaScript object into a name/value-pair notation... –  carlosfigueira May 15 at 2:07
 
I'm curious to see the example where you say that the default formatter is generating invalid JSON - could you send an example to carlosfigueira at outlook dot com? If this is the case, it should be a bug in the framework, and I know some people who work on the ASP.NET team, and I'd be glad to pass it along to them. –  carlosfigueira May 15 at 2:09
add comment

You can take reference the link below. Hope this help.

And here is sample using Json.net with web API.

share|improve this answer
 
The link you provided does not work, or at least doesn't seem to work. It references things that no longer exists with VS2012. –  Yanick Rochon May 14 at 17:39
 
alright, I managed to make it work by replacing some methods signature, but the deserializer does not seem to work. I'll update the question to reflect what I'm trying to do –  Yanick Rochon May 14 at 18:16
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.