Api控制器中的JSON.Net w /包含Dictionary的param始终为null

Yan*_*hon 2 c# api json.net asp.net-mvc-4 asp.net-web-api

我已经看到一些声称可以工作的教程,但是它们已经过时或根本不起作用.

如何使用JSON.Net序列化和反序列化从API控制器接收和发送的数据?

我们正在使用VS2012.

更新

我有这样的模特

public class SearchModel
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public Dictionary<string, object> Terms { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

像这样的Api控制器

public class ModelSearchApiController : ApiController
{

     public List<Model> Get([FromUri] SearchModel search)
     {
         return new List<Model>();
     }
}
Run Code Online (Sandbox Code Playgroud)

但是,search在Ajax请求中提供了正确的值,该属性Terms始终是一个空字典.

我知道我们可以提供一个值,[ { Key:"foo", Value:123 } ]但为什么我不能只传递一个普通的JSON对象(即{ foo:123 })??? 为什么它可以将一个序列Dictionary化为一个漂亮的标准JSON对象,但不能采用那个完全相同的对象并重新创建一个Dictionary.这让我感到很震惊.

编辑

换句话说,如果浏览器发送以下参数:

 pageIndex: 0
 pageSize: 100
 terms[foo]: Bar
 terms[buz]: 1234
Run Code Online (Sandbox Code Playgroud)

什么是必需的对象签名?因为上面提到的对象不起作用,字典只是空的.

car*_*ira 6

JSON.NET ASP.NET Web API 的默认序列化程序 - 它可以在JSON和CLR对象之间进行转换,并且对所有JSON输入都是如此.但是,您并没有尝试将JSON输入转换为SearchModel - 您尝试将类似于application/x-www-form-urlencoded的基于URI的格式转换为CLR类型的SearchModel,并且JSON.NET不支持(它不是JSON!).通常,序列化程序用于将(在传入的请求中)从请求主体转换为action参数.

让我们看看下面的这个(完整的)示例(假设默认路由为"api/{controller}").它与您的问题非常相似,但除了GET方法之外,我还添加了一个Post方法.

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; }
}
Run Code Online (Sandbox Code Playgroud)

如果您将此请求发送到服务器:

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"}}
Run Code Online (Sandbox Code Playgroud)

它将按照您的预期绑定到SearchModel参数 - 该Terms属性将是一个包含两个条目的字典(foo = bar,foo2 = bar2).

现在,对于GET参数.ASP.NET Web API具有模型绑定器值提供程序的概念,它将是将查询字符串转换为操作参数的组件.默认绑定器/提供程序不支持复杂类型内的字典的"任意"名称/值对语法*.正如您所指出的,您可以使用键/值对语法,这将被理解,如下所示.

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
Run Code Online (Sandbox Code Playgroud)

现在,对于您的问题,您有两种选择.您可以更改API以使用自定义模型绑定器或值提供程序,它知道如何理解"简单"名称/值语法,如下所示:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个选择是在发送到服务器之前使用类似下面的功能在客户端上预处理输入数据.

function objToKVPArray(obj) {
    var result = [];
    var k;
    for (k in obj) {
        if (obj.hasOwnProperty(k)) {
            result.push({ key: k, value: obj[k] });
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

  • 让我们设置一些术语,我想我们正在谈论不同的事情.JSON是一种有线格式."JSON对象"是这种格式的东西:`{"name":<value>}`,其中`<value>`可以是字符串,数字,布尔值,null或其他JSON对象或数组.GET操作接收的输入不是JSON对象.它是*JavaScript*对象到名称/值对符号的转换... (2认同)