我看过那些声称可以使用的教程,但是它们已经过时或者根本不起作用。
如何使用JSON.Net序列化和反序列化从API控制器接收和发送的数据?
我们正在使用VS2012。
我有这样的模型
public class SearchModel
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
像这样的Api控制器
public class ModelSearchApiController : ApiController
{
public List<Model> Get([FromUri] SearchModel search)
{
return new List<Model>();
}
}
但是,search
在Ajax请求中提供了正确的值集,属性Terms
始终是一个空字典。
我知道我们可以提供类似[ { Key:"foo", Value:123 } ]
的值,但为什么我不能只传递一个普通的JSON对象(即{ foo:123 }
)???为什么它可以将Dictionary
序列化为一个漂亮的标准JSON对象,但不能将完全相同的对象重新创建Dictionary
。这让我很感兴趣。
换句话说,如果浏览器发送以下参数:
pageIndex: 0
pageSize: 100
terms[foo]: Bar
terms[buz]: 1234
所需的对象签名是什么?因为上面提到的对象不起作用,字典只是空的。
答案 0 :(得分:6)
JSON.NET 是ASP.NET Web API的默认序列化程序 - 它可以在 JSON 和CLR对象之间进行转换,并为所有JSON输入执行此操作。但是,您并没有尝试将JSON输入转换为SearchModel - 您尝试将类似于application / x-www-form-urlencoded的基于URI的格式转换为CLR类型的SearchModel,并且JSON.NET不支持(它不是JSON!)。通常,序列化程序用于将来自请求正文的传入请求转换为动作参数。
让我们看看下面的这个(完整)示例(假设默认路由为"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; }
}
如果您将此请求发送到服务器:
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"}}
它将按照您的预期绑定到SearchModel参数 - Terms
属性将是一个包含两个条目的字典(foo = bar,foo2 = bar2)。
现在,对于GET参数。 ASP.NET Web API具有model binders和value provider的概念,它将是将查询字符串转换为操作参数的组件。默认绑定器/提供程序不支持复杂类型内的字典的“任意”名称/值对语法*。正如您所指出的,您可以使用键/值对语法,这将被理解,如下所示。
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
现在,对于您的问题,您有两种选择。您可以更改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;
}
}
另一种选择是在发送到服务器之前使用类似下面的功能在客户端上预处理输入数据。
function objToKVPArray(obj) {
var result = [];
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
result.push({ key: k, value: obj[k] });
}
}
return result;
}
答案 1 :(得分:0)