C# 包含字典的参数的Api控制器中的JSON.Net始终为空

C# 包含字典的参数的Api控制器中的JSON.Net始终为空,c#,api,asp.net-mvc-4,asp.net-web-api,json.net,C#,Api,Asp.net Mvc 4,Asp.net Web Api,Json.net,我看到一些教程声称是有效的,但它们已经过时或根本不起作用 如何使用JSON.Net序列化和反序列化从API控制器接收和发送的数据 我们正在使用VS2012 更新 我有一个这样的模型 public class SearchModel { public int PageIndex { get; set; } public int PageSize { get; set; } public Dictionary<string, object> Terms { get

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

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

我们正在使用VS2012

更新 我有一个这样的模型

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

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

所需的对象签名是什么?因为上面提到的对象不起作用,而且字典是空的。

您可以参考下面的内容。希望这有帮助


这里是将Json.net与web API一起使用。

Json.net是ASP.net web API的默认序列化程序——它可以在Json和CLR对象之间进行转换,并且可以对所有Json输入进行转换。但是,您并不是试图将JSON输入转换为SearchModel,而是试图将基于URI的格式(类似于application/x-www-form-urlencoded)转换为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; }
}
正如您所期望的,它将绑定到SearchModel参数,
Terms
属性将是一个包含两个条目(foo=bar,foo2=bar2)的字典

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

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;
    }
}

JSON.NET是API控制器的默认序列化程序(即从
ApiController
派生的类)。你不需要做任何其他事情,它应该只是工作。你有什么特别的问题吗?@Carlosfiguera,有。我的一个模型声明了一个
JsonConverter
,而反序列化程序不使用它。这让我假设Json.net不用于反序列化我的数据。@ckozl,很抱歉告诉你这一点,但不管人们怎么说,Json.net绝对不是默认的序列化程序。默认序列化程序为对象提供了一个糟糕的非标准伪JSON响应(但仍然能够正确地序列化字典)。我只是用我自己的替换了它。问题是它不会从请求中读取任何内容。同样,JSON.NET是API控制器的默认序列化程序。如果定义JsonConverter,它将用于JSON输入。更多详细信息,请参见我的答案。您提供的链接不起作用,或者至少似乎不起作用。它引用了VS2012中不再存在的东西。好吧,我通过替换一些方法签名使它工作,但是反序列化器似乎不工作。我将更新这个问题以反映我想做的事情。我不知道为什么每个人都说JSON.Net是默认的JsonFormatter。对我来说,我必须替换它,因为使用默认格式化程序给出了无效的JSON符号,并且不理解JSON.Net的class/properties属性。无论如何,继续前进。我需要与模型的输入具有相同的输出(我从服务器获得的信息应该与发送回它的信息相同),因此我只需使用一个自定义类来扩展
字典
,并使用
[JsonArrayAttribute]
。这使我有了C#能够理解的对象
{key,value}
数组(丑陋且非标准)。如果您提出一个解决方案,允许标准JSON对象作为
字典
传递给C#,那么我将检查您的答案是否为可接受的解决方案。至于现在,这仅仅是一个黑客行为,因为我不喜欢这个解决方案(键/值对而不是真正的JSON对象)。让我们设置一些术语,我认为我们谈论的是不同的事情。JSON是一种有线格式。“JSON对象”的格式如下:
{“name”:}
,其中
可以是字符串、数字、布尔值、null或其他JSON对象或数组。GET操作接收的输入不是JSON对象。这是将JavaScript对象转换为名称/值对表示法…我很好奇看到这样一个示例,您说默认格式化程序生成的JSON无效-您能给outlook.com上的carlosfigueira发送一个示例吗?如果是这样的话,它应该是框架中的一个bug,我认识一些在ASP.NET团队中工作的人,我很乐意将它传递给他们。
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
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;
}