【问题标题】:In ASP.NET MVC, deserialize JSON prior to or in controller's action method在 ASP.NET MVC 中,在控制器的操作方法之前或之中反序列化 JSON
【发布时间】:2010-12-01 06:52:57
【问题描述】:

我正在开发一个将 JSON 对象(使用 jQuery Post 方法)发布到服务器端的网站。

{ 
    "ID" : 1,
    "FullName" : {
       "FirstName" : "John",
       "LastName" : "Smith"
    }
}

同时,我在服务端为这个数据结构写了类。

public class User
{
    public int ID { get; set; }
    public Name FullName { get; set;}
}

public class Name
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

当我在控制器类中使用以下代码运行网站时,FullName 属性不会被反序列化。我究竟做错了什么?

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(User user)
{
    // At this point, user.FullName is NULL. 

    return View();
}

【问题讨论】:

  • MVC 不支持开箱即用的 JSON 反序列化,但我们正在考虑为 v2.0 添加它。同时,您可以使用 JavaScriptSerializer 将请求正文转换为完全水合的 User 对象。
  • @Levi - 这应该是一个回答帖子;)
  • 这很奇怪;不知何故,ID 属性被正确反序列化。如果我使用 JavaScriptSerializer,Submit() 的输入参数会是 Object 类型吗?
  • 我只是使用 jQuery 表单库,它只是像普通表单一样发布它。如果你不能这样做,我会使用自定义模型绑定器。

标签: c# jquery asp.net-mvc json


【解决方案1】:

我通过实施动作过滤器解决了我的问题;下面提供了代码示例。从研究中,我了解到还有另一种解决方案,即模型绑定器,如上述 takepara 所述。但我真的不知道这两种方法的利弊。

感谢 Steve Gentile 的 blog post 提供此解决方案。

public class JsonFilter : ActionFilterAttribute
    {
        public string Parameter { get; set; }
        public Type JsonDataType { get; set; }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
            {
                string inputContent;
                using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
                {
                    inputContent = sr.ReadToEnd();
                }

                var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
                filterContext.ActionParameters[Parameter] = result;
            }
        }
    }

[AcceptVerbs(HttpVerbs.Post)]
[JsonFilter(Parameter="user", JsonDataType=typeof(User))]
public ActionResult Submit(User user)
{
    // user object is deserialized properly prior to execution of Submit() function

    return View();
}

【讨论】:

  • awww snap,133t MVC 技能的weilin,你最好在星期一教我; )
  • 这个方法比自定义 ModelBinder 有一个显着的优势,你可以定义要反序列化的类型。使用自定义 ModelBinder,它是硬编码的,因此仅对一种类型有用。
【解决方案2】:

1.创建自定义模型绑定器

  public class UserModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      User model;

      if(controllerContext.RequestContext.HttpContext.Request.AcceptTypes.Contains("application/json"))
      {
        var serializer = new JavaScriptSerializer();
        var form = controllerContext.RequestContext.HttpContext.Request.Form.ToString();
        model = serializer.Deserialize<User>(HttpUtility.UrlDecode(form));
      }
      else
      {
        model = (User)ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);
      }

      return model;
    }
  }

2.在application_start事件中添加模型绑定

  ModelBinders.Binders[typeof(User)] = new UserModelBinder();

3.在查看客户端 JavaScript 代码中使用 jQuery $.get/$.post。

  <% using(Html.BeginForm("JsonData","Home",new{},FormMethod.Post, new{id="jsonform"})) { %>

    <% = Html.TextArea("jsonarea","",new {id="jsonarea"}) %><br />

    <input type="button" id="getjson" value="Get Json" />
    <input type="button" id="postjson" value="Post Json" />
  <% } %>
  <script type="text/javascript">
    $(function() {
      $('#getjson').click(function() {
        $.get($('#jsonform').attr('action'), function(data) {
          $('#jsonarea').val(data);
        });
      });

      $('#postjson').click(function() {
        $.post($('#jsonform').attr('action'), $('#jsonarea').val(), function(data) {
          alert("posted!");
        },"json");
      });
    });
  </script>

【讨论】:

  • 你的意思可能是“ContentType”而不是“AcceptTypes”
【解决方案3】:

你可以试试Json.NETdocumentation 还不错,应该可以do what you need。您还需要获取JsonNetResult,因为它返回一个可在 ASP.NET MVC 应用程序中使用的 ActionResult。它非常易于使用。

Json.NET 也适用于日期序列化。有关 can be found here 的更多信息。

希望这会有所帮助。

【讨论】:

    【解决方案4】:

    试试这个;

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Submit(FormCollection collection)
    {
        User submittedUser = JsonConvert.DeserializeObject<User>(collection["user"]); 
        return View();
    }
    

    【讨论】:

      【解决方案5】:

      经过一番研究,我发现 Takepara 的解决方案是用 Newtonsoft 的 Json.NET 替换默认 MVC JSON 反序列化器的最佳选择。它也可以推广到程序集中的所有类型,如下所示:

      using Newtonsoft.Json;
      
      namespace MySite.Web
      {
          public class MyModelBinder : IModelBinder
          {
              // make a new Json serializer
              protected static JsonSerializer jsonSerializer = null;
      
              static MyModelBinder()
              {
                  JsonSerializerSettings settings = new JsonSerializerSettings();
                  // Set custom serialization settings.
                  settings.DateTimeZoneHandling= DateTimeZoneHandling.Utc;
                  jsonSerializer = JsonSerializer.Create(settings);
              }
      
              public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
              {
                  object model;
      
                  if (bindingContext.ModelType.Assembly == "MyDtoAssembly")
                  {
                      var s = controllerContext.RequestContext.HttpContext.Request.InputStream;
                      s.Seek(0, SeekOrigin.Begin);
                      using (var sw = new StreamReader(s))
                      {
                          model = jsonSerializer.Deserialize(sw, bindingContext.ModelType);
                      }
                  }
                  else
                  {
                      model = ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);
                  }
                  return model;
              }
          }
      }
      

      那么,在Global.asax.csApplication_Start()

              var asmDto = typeof(SomeDto).Assembly;
              foreach (var t in asmDto.GetTypes())
              {
                  ModelBinders.Binders[t] = new MyModelBinder();
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-26
        • 1970-01-01
        • 2011-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-11
        • 1970-01-01
        相关资源
        最近更新 更多