JSON.NET'i ASP.NET MVC 3'te varsayılan JSON serileştiricisi olarak kullanmak mümkün mü?


101

JSON.NET'i ASP.NET MVC 3'te varsayılan JSON serileştiricisi olarak kullanmak mümkün müdür ?

Araştırmalarıma göre, bunu gerçekleştirmek için tek yol olduğunu görünüyor ActionResult uzatmak olarak MVC3 içinde JsonResult sanal değildir ...

ASP.NET MVC 3 ile JSON'a serileştirmek için takılabilir bir sağlayıcı belirtmenin bir yolu olacağını umuyordum.

Düşünceler?


Yanıtlar:


106

Bunu yapmanın en iyi yolunun - bağlantılarınızda açıklandığı gibi - ActionResult'u genişletmek veya JsonResult'u doğrudan genişletmek olduğuna inanıyorum.

Doğru olmayan denetleyicide sanal olmayan JsonResult yöntemine gelince, sadece doğru aşırı yüklemeyi seçin. Bu iyi çalışıyor:

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

EDIT 1 : Bir JsonResult uzantısı ...

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

DÜZENLEME 2 : Verilerin boş olduğu kontrolünü aşağıdaki önerilere göre kaldırdım. Bu, JQuery'nin daha yeni sürümlerini mutlu etmeli ve yanıtın koşulsuz olarak serileştirilmesinden arındırılabileceği için yapılması gereken mantıklı bir şey gibi görünüyor. Bununla birlikte, bunun ASP.NET MVC'den JSON yanıtları için varsayılan davranış olmadığını unutmayın; bu, veri olmadığında boş bir dizeyle yanıt verir.


1
Kod, tanımlanmamış olan MySpecialContractResolver'a başvuruyor. Bu soru buna yardımcı oluyor (ve çözmem gereken sorunla çok ilgiliydi): stackoverflow.com/questions/6700053/…
Elliveny

1
Harika cevap için teşekkürler. Neden if (Data == null) döndürür; ? Benim kullanım durumum için, JSON standardı ne olursa olsun, Json.Net'in null için bile ("boş" döndürerek) sadakatle yaptığı geri almak istedim. : Örneğin jQuery 1.9.1 ile - boş değerler müdahale ederek standart sapma ve mansap sorunlara neden bunlar için boş dize geri gönderme sona stackoverflow.com/a/15939945/176877
Chris Moschini

1
@Chris Moschini: Kesinlikle haklısın. Boş bir dizge döndürmek yanlıştır. Ama o zaman json null değerini mi yoksa boş bir json nesnesini mi döndürmeli? Bir nesnenin beklendiği bir değeri döndürmenin de sorunsuz olduğundan emin değilim. Ancak her iki durumda da mevcut kod bu açıdan iyi değil.
asgerhallas

1
Json.Net'te IE9 ve altındaki Json.Net'in ürettiği ISO 8601 Tarihlerini ayrıştırmada başarısız olmasına neden olan bir hata var. Bunun düzeltmesi bu yanıta dahil edilmiştir: stackoverflow.com/a/15939945/176877
Chris Moschini

1
@asgerhallas, @Chris Moschini Peki ya varsayılan asp.net mvc JsonResult denetimi if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);? Bu kontrolü cevaba eklemem gerektiğini düşünüyorum (dahili olmadan MvcResources.JsonRequest_GetNotAllowedancak bazı özel mesajlarla) Ayrıca, diğer 2 varsayılan asp.net mvc kontrolleri - MaxJsonLength ve RecursionLimit? Json.net kullanırsak onlara ihtiyacımız var mı?
chromigo

60

Bunu bir temel kontrolöre veya enjeksiyona ihtiyaç duymadan uyguladım.

JsonResult'u bir JsonNetResult ile değiştirmek için eylem filtreleri kullandım.

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 () içinde şunu eklemeniz gerekir:

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

Tamamlama aşkına, işte başka bir yerden aldığım ve doğru buharlama desteği almak için biraz değiştirdiğim JsonNetResult uzantı sınıfım:

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
Bu güzel bir çözüm. O kadar yerli yapar return Json()etkisi kullanımları Json.Net içinde.
OneHoopyFrood

1
Bunun nasıl merak herkes için, karşılar JsonResultgelen Json()bir etmek ve dönüşürse o JsonNetResult. Bunu as, dönüşüm mümkün değilse boş döndüren anahtar kelimeyi kullanarak yapar . Çok şık. Gryffindor'a 10 puan!
OneHoopyFrood

4
Yine de soru, varsayılan serileştirici yakalanmadan önce nesne üzerinde çalışıyor mu?
OneHoopyFrood

Bu harika bir cevap - en fazla esneklikle. Projem zaten ön uçta her türlü manuel çözümü yaptığından, küresel bir filtre ekleyemedim - bu daha büyük bir değişiklik gerektirecekti. Sonunda, sorunu yalnızca denetleyicimin eylemlerindeki özniteliği kullanarak gerektiğinde denetleyici eylemlerinde çözdüm. Ancak, onu aradım - [BetterJsonHandler]:-).
Simcha Khabinsky

this.Json (null); hala hiçbir şey
dönmüyor

27

Newtonsoft'un JSON dönüştürücüsünü kullanın:

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

7
Bunun hileli olup olmadığından emin değilim, ama kutsal saçmalık, sadece aptal bir json dizesi döndürmek için uzantı sınıfları oluşturmaktan daha kolaydır.
dennis.sheppard

21

Bunun soru yanıtlandıktan sonra olduğunu biliyorum, ancak denetleyicilerimi başlatmak için bağımlılık enjeksiyonu kullandığım için farklı bir yaklaşım kullanıyorum.

IActionInvoker'i (denetleyicinin ControllerActionInvoker Özelliğini enjekte ederek) InvokeActionMethod yöntemini geçersiz kılan bir sürümle değiştirdim.

Bu, denetleyici mirasında değişiklik olmadığı anlamına gelir ve MVC4'e yükselttiğimde DI kapsayıcısının TÜM denetleyiciler için kaydını değiştirerek kolayca kaldırılabilir.

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

--- DÜZENLE - Denetleyiciler için kapsayıcı kaydını göstermek için güncellendi. Burada Unity kullanıyorum.

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

Güzel, ama onu nasıl kullanıyorsun? Ya da daha iyisi nasıl enjekte ettiniz?
Adaptabi

.Serialize () Akış biçimini kullanmak için +1. JsonConvert'ı diğer en iyi cevap gibi kullanabileceğinizi belirtecektim, ancak yaklaşımınız kademeli olarak uzun / büyük nesneleri yayar - bu, özellikle aşağı akıştaki istemci kısmi yanıtları kaldırabiliyorsa, bu ücretsiz bir performans artışıdır.
Chris Moschini

1
güzel uygulama. Cevap bu olmalı!
Kat Lim Ruiz

İyi gidiyor, temel kontrolör kullandığım tek şey buydu.
Chris Diver

gerçekten güzel - bu, Json () işlevini geçersiz kılmaktan çok daha iyidir, çünkü bir JsonResult döndürdüğünüz her konumda bu devreye girecek ve sihir yapacaktır. DI kullanmayanlar için, temel denetleyicinize korumalı geçersiz kılma IActionInvoker CreateActionInvoker () {yeni JsonNetActionInvoker ();} ekleyin
Avi Pinto

13

Https://stackoverflow.com/users/183056/sami-beyoglu adresindeki cevabı genişleterek , İçerik türünü ayarlarsanız, jQuery, döndürülen verileri sizin için bir nesneye dönüştürecektir.

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

Teşekkür ederim, hibrit bir karışımım var ve bu benim için işe yarayacak tek şey.
done_merson

Bunu JSON.NET ile şu şekilde kullandım: JObject jo = GetJSON(); return Content(jo.ToString(), "application/json");
John Mott

6

Postam birine yardımcı olabilir.

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

Gerçek bir çözüm buluyordum ve tek doğru cevap sendin
Richard Aguirre

Teşekkürler. Benimkini zaten uyguladığım için BaseController, bu en düşük etki değişimiydi - sadece sınıfı eklemek ve güncellemek zorunda kaldım BaseController.
AndrewP

4

Web hizmeti işlemlerini tür açısından güvenli ve basit hale getiren bir sürüm yaptım. Bunu şu şekilde kullanırsın:

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

Sınıf:

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

ama neden güçlü bir JsonResult türüne sahip olmak istersiniz? : D anonim tür sonuçlarını kaybediyorsunuz ve yine de C # sınıflarını kullanmadığı için istemci tarafında hiçbir şey kazanmıyorsunuz?
mikus

1
@mikus Sunucu tarafında türler güvenlidir: yöntem MyDataContract türünü döndürmelidir. Müşteri tarafına tam olarak hangi veri yapısının döndürüldüğünü açıklar. Aynı zamanda kısa ve okunabilir - JsonResult <T>, Json'a döndürülen herhangi bir türü otomatik olarak dönüştürür ve hiçbir şey yapmanız gerekmez.
Curtis Yallop
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.