使用JSON.NET作为ASP.NET MVC 3中的默认JSON序列化程序-可以吗?


101

是否可以将JSON.NET用作ASP.NET MVC 3中的默认JSON序列化程序?

根据我的研究,似乎完成此操作的唯一方法是扩展ActionResult,因为MVC3中的JsonResult不是虚拟的 ...

我希望使用ASP.NET MVC 3,可以有一种方法来指定可插拔的提供程序以序列化为JSON。

有什么想法吗?


Answers:


106

我相信最好的方法是-如您的链接中所述-扩展ActionResult或直接扩展JsonResult。

至于JsonResult在控制器上不是虚拟的不是真的方法,只需选择正确的重载即可。这很好用:

protected override JsonResult Json(object data, string contentType, Encoding contentEncoding)

编辑1:一个JsonResult扩展...

public class JsonNetResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(ContentType) 
            ? ContentType 
            : "application/json";

        if (ContentEncoding != null)
            response.ContentEncoding = ContentEncoding;

        // If you need special handling, you can call another form of SerializeObject below
        var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented);
        response.Write(serializedObject);
    }

编辑2:按照以下建议,我删除了Data为null的检查。这应该使新版本的JQuery感到高兴,并且看起来很明智,因为可以无条件反序列化响应。但是请注意,这不是来自ASP.NET MVC的JSON响应的默认行为,当没有数据时,它将以空字符串进行响应。


1
该代码引用MySpecialContractResolver,但未定义。这个问题有助于我们与(并且非常相关的问题,我必须解决):stackoverflow.com/questions/6700053/...
Elliveny

1
感谢您的出色回答。为什么if(Data == null)返回;?对于我的用例,我想找回JSON标准是什么,Json.Net忠实地做到了,即使是null(返回“ null”)。通过截取空值,您最终会为这些空值发送回空字符串,这会偏离标准并导致下游问题-例如jQuery 1.9.1:stackoverflow.com/a/15939945/176877
Chris Moschini 2013年

1
@克里斯·莫斯奇尼(Chris Moschini):你绝对正确。返回空字符串是错误的。但是它应该返回json值null还是空的json对象呢?我不确定返回期望对象的值是否也没有问题。但是无论哪种方式,当前的代码在这方面都不好。
asgerhallas 2013年

1
Json.Net中存在一个错误,该错误导致IE9及以下版本无法解析Json.Net生成的ISO 8601日期。对此问题的解决方案包含在此答案中:stackoverflow.com/a/15939945/176877
克里斯·莫斯基尼

1
@ asgerhallas,@ Chris Moschini默认的asp.net mvc JsonResult检查if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);如何?我认为需要在答案中添加此检查(没有内部MvcResources.JsonRequest_GetNotAllowed但有一些自定义消息)另外,另外2个默认的asp.net mvc检查-MaxJsonLength和RecursionLimit又如何呢?如果使用json.net,是否需要它们?
chromigo '16

60

我无需基本控制器或注入就实现了这一点。

我使用动作过滤器将JsonResult替换为JsonNetResult。

public class JsonHandlerAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
       var jsonResult = filterContext.Result as JsonResult;

        if (jsonResult != null)
        {
            filterContext.Result = new JsonNetResult
            {
                ContentEncoding = jsonResult.ContentEncoding,
                ContentType = jsonResult.ContentType,
                Data = jsonResult.Data,
                JsonRequestBehavior = jsonResult.JsonRequestBehavior
            };
        }

        base.OnActionExecuted(filterContext);
    }
}

在Global.asax.cs Application_Start()中,您需要添加:

GlobalFilters.Filters.Add(new JsonHandlerAttribute());

为了完成起见,这是我从其他地方拾取的JsonNetResult扩展类,并对其进行了一些修改以获取正确的流支持:

public class JsonNetResult : JsonResult
{
    public JsonNetResult()
    {
        Settings = new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Error
        };
    }

    public JsonSerializerSettings Settings { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            throw new InvalidOperationException("JSON GET is not allowed");

        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;

        if (this.ContentEncoding != null)
            response.ContentEncoding = this.ContentEncoding;
        if (this.Data == null)
            return;

        var scriptSerializer = JsonSerializer.Create(this.Settings);
        scriptSerializer.Serialize(response.Output, this.Data);
    }
}

1
这是一个很好的解决方案。这样就可以使本机return Json()实际上使用Json.Net。
OneHoopyFrood

1
对于任何想知道它如何工作的人,它会拦截JsonResultfrom Json()并将其转换为a JsonNetResultas如果无法进行转换,则使用返回null 的关键字来实现。很漂亮 格兰芬多10分!
OneHoopyFrood

4
但是,问题是,默认序列化程序是否在对象被拦截之前在对象上运行?
OneHoopyFrood

这是一个绝佳的答案-具有最大的灵活性。由于我的项目已经在前端执行了各种手动解决方案,因此我无法添加全局过滤器-这将需要进行较大的更改。最后,仅在需要时通过使用控制器动作上的属性解决了控制器动作上的问题。但是,我称它为- [BetterJsonHandler]:-)。
Simcha Khabinsky

返回this.Json(null); 仍然没有返回任何东西
Brunis '17

27

使用Newtonsoft的JSON转换器:

public ActionResult DoSomething()
{
    dynamic cResponse = new ExpandoObject();
    cResponse.Property1 = "value1";
    cResponse.Property2 = "value2";
    return Content(JsonConvert.SerializeObject(cResponse), "application/json");
}

7
不知道这是不是很hacky,但是比起创建扩展类,只是返回一个愚蠢的json字符串更容易。
dennis.sheppard 2014年

21

我知道在回答问题后一切都很好,但是我使用了一种不同的方法,因为我使用依赖注入来实例化我的控制器。

我已经用覆盖InvokeActionMethod方法的版本替换了IActionInvoker(通过注入控制器的ControllerActionInvoker属性)。

这意味着无需更改控制器继承,并且当我通过更改所有控制器的DI容器的注册升级到MVC4时,可以轻松删除它

public class JsonNetActionInvoker : ControllerActionInvoker
{
    protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
    {
        ActionResult invokeActionMethod = base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);

        if ( invokeActionMethod.GetType() == typeof(JsonResult) )
        {
            return new JsonNetResult(invokeActionMethod as JsonResult);
        }

        return invokeActionMethod;
    }

    private class JsonNetResult : JsonResult
    {
        public JsonNetResult()
        {
            this.ContentType = "application/json";
        }

        public JsonNetResult( JsonResult existing )
        {
            this.ContentEncoding = existing.ContentEncoding;
            this.ContentType = !string.IsNullOrWhiteSpace(existing.ContentType) ? existing.ContentType : "application/json";
            this.Data = existing.Data;
            this.JsonRequestBehavior = existing.JsonRequestBehavior;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if ((this.JsonRequestBehavior == JsonRequestBehavior.DenyGet) && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                base.ExecuteResult(context);                            // Delegate back to allow the default exception to be thrown
            }

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = this.ContentType;

            if (this.ContentEncoding != null)
            {
                response.ContentEncoding = this.ContentEncoding;
            }

            if (this.Data != null)
            {
                // Replace with your favourite serializer.  
                new Newtonsoft.Json.JsonSerializer().Serialize( response.Output, this.Data );
            }
        }
    }
}

---编辑-更新为显示控制器的容器注册。我在这里使用Unity。

private void RegisterAllControllers(List<Type> exportedTypes)
{
    this.rootContainer.RegisterType<IActionInvoker, JsonNetActionInvoker>();
    Func<Type, bool> isIController = typeof(IController).IsAssignableFrom;
    Func<Type, bool> isIHttpController = typeof(IHttpController).IsAssignableFrom;

    foreach (Type controllerType in exportedTypes.Where(isIController))
    {
        this.rootContainer.RegisterType(
            typeof(IController),
            controllerType, 
            controllerType.Name.Replace("Controller", string.Empty),
            new InjectionProperty("ActionInvoker")
        );
    }

    foreach (Type controllerType in exportedTypes.Where(isIHttpController))
    {
        this.rootContainer.RegisterType(typeof(IHttpController), controllerType, controllerType.Name);
    }
}

public class UnityControllerFactory : System.Web.Mvc.IControllerFactory, System.Web.Http.Dispatcher.IHttpControllerActivator
{
    readonly IUnityContainer container;

    public UnityControllerFactory(IUnityContainer container)
    {
        this.container = container;
    }

    IController System.Web.Mvc.IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        return this.container.Resolve<IController>(controllerName);
    }

    SessionStateBehavior System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
    {
        return SessionStateBehavior.Required;
    }

    void System.Web.Mvc.IControllerFactory.ReleaseController(IController controller)
    {
    }

    IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        return this.container.Resolve<IHttpController>(controllerType.Name);
    }
}

很好,但是您如何使用它呢?还是更好的是您如何注入的?
Adaptabi

+1以使用.Serialize()的Stream形式。我要指出的是,您可以像使用其他最佳答案一样使用JsonConvert,但是您的方法会逐渐流出长/大对象-这是免费的性能提升,尤其是在下游客户端可以处理部分响应的情况下。
克里斯·莫斯基尼

1
很好的实现。这应该是答案!
Kat Lim Ruiz 2013年

一切顺利,这是我使用基本控制器的唯一目的。
克里斯·迪弗

真的很好-这比重写Json()函数要好得多,因为在您将返回JsonResult的每个位置,这都会启动并起到神奇的作用。对于那些不使用DI的用户,只需添加受保护的重写IActionInvoker CreateActionInvoker(){返回新的JsonNetActionInvoker();}至您的基本控制器
Avi Pinto

13

扩展来自https://stackoverflow.com/users/183056/sami-beyoglu的答案,如果您设置了Content类型,那么jQuery将能够为您将返回的数据转换为对象。

public ActionResult DoSomething()
{
    dynamic cResponse = new ExpandoObject();
    cResponse.Property1 = "value1";
    cResponse.Property2 = "value2";
    return Content(JsonConvert.SerializeObject(cResponse), "application/json");
}

谢谢,我有混合动力,这是唯一对我有用的东西。
done_merson15年

我将其用于JSON.NET,如下所示: JObject jo = GetJSON(); return Content(jo.ToString(), "application/json");
John Mott

6

我的帖子可能会对某人有所帮助。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace MultipleSubmit.Service
{
    public abstract class BaseController : Controller
    {
        protected override JsonResult Json(object data, string contentType,
            Encoding contentEncoding, JsonRequestBehavior behavior)
        {
            return new JsonNetResult
            {
                Data = data,
                ContentType = contentType,
                ContentEncoding = contentEncoding,
                JsonRequestBehavior = behavior
            };
        }
    }
}


using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MultipleSubmit.Service
{
    public class JsonNetResult : JsonResult
    {
        public JsonNetResult()
        {
            Settings = new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Error
            };
        }
        public JsonSerializerSettings Settings { get; private set; }
        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals
(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                throw new InvalidOperationException("JSON GET is not allowed");
            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = string.IsNullOrEmpty(this.ContentType) ? 
"application/json" : this.ContentType;
            if (this.ContentEncoding != null)
                response.ContentEncoding = this.ContentEncoding;
            if (this.Data == null)
                return;
            var scriptSerializer = JsonSerializer.Create(this.Settings);
            using (var sw = new StringWriter())
            {
                scriptSerializer.Serialize(sw, this.Data);
                response.Write(sw.ToString());
            }
        }
    }
} 

public class MultipleSubmitController : BaseController
{
   public JsonResult Index()
    {
      var data = obj1;  // obj1 contains the Json data
      return Json(data, JsonRequestBehavior.AllowGet);
    }
}    

我正在寻找一个真正的解决方案,而您是唯一的正确答案
理查德·阿奎尔

谢谢。已经实现了我自己的方法BaseController,这对chang的影响是最小的-只需添加类和更新即可BaseController
AndrewP

4

我制作了一个使Web服务操作类型安全且简单的版本。您可以这样使用它:

public JsonResult<MyDataContract> MyAction()
{
    return new MyDataContract();
}

班上:

public class JsonResult<T> : JsonResult
{
    public JsonResult(T data)
    {
        Data = data;
        JsonRequestBehavior = JsonRequestBehavior.AllowGet;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        // Use Json.Net rather than the default JavaScriptSerializer because it's faster and better

        if (context == null)
            throw new ArgumentNullException("context");

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(ContentType)
            ? ContentType
            : "application/json";

        if (ContentEncoding != null)
            response.ContentEncoding = ContentEncoding;

        var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented);
        response.Write(serializedObject);
    }

    public static implicit operator JsonResult<T>(T d)
    {
        return new JsonResult<T>(d);
    }
}

但是为什么要拥有强类型的JsonResult?:D您放开匿名类型的结果,而在客户端却一无所获,因为它还是不使用C#类?
mikus 2015年

1
@mikus在服务器端是类型安全的:该方法必须返回类型MyDataContract。它使客户端清楚地知道返回了什么数据结构。它也简洁易读-JsonResult <T>可以自动将任何类型转换为返回给Json的类型,而您无需执行任何操作。
柯蒂斯·雅洛普
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.