MVC3 Controller with specific JsonConverter

天涯浪子 提交于 2019-12-21 22:25:48

问题


Here's the setup:

I have some MVC Controllers that are intended to be consumed by jQuery ajax requests. A normal request would seem somewhat like this:

$.ajax("/Solicitor/AddSolicitorToApplication", {
    data: putData,
    type: "POST", contentType: "application/json",
    success: function (result) {
       //My success callback
        }
    }
});

My controller looks like this:

[HttpPost]
public ActionResult InsertLoanApplication(MortgageLoanApplicationViewModel vm)
{
   var mortgageLoanDTO = vm.MapToDTO();
   return Json(_mortgageLoanService.UpdateMortgageLoanApplication(mortgageLoanDTO),   JsonRequestBehavior.DenyGet);
}

This works perfectly fine with most objects passed to the controller, except that in this specific case one of the properties of the object being passed needs to be deserialized in a specific way.

I've added a JsonConverter that I've used previously with the MVC4 Web API, but in this case I need to apply it to regular mvc controllers.

I tried registering the JsonConverter in my global.asax like this:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new GrizlyStringConverter());

But so far haven't been able to deserialize the object.


回答1:


You should replace the built-in JsonValueProviderFactory class with a custom one if you want to use Json.NET when binding JSON requests to view models.

You could write one as shown in this gist:

public sealed class JsonDotNetValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
        {
            return null;
        }

        using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
        {
            var bodyText = reader.ReadToEnd();

            return String.IsNullOrEmpty(bodyText)
                ? null :
                new DictionaryValueProvider<object>(
                    JsonConvert.DeserializeObject<ExpandoObject>(
                        bodyText,
                        new ExpandoObjectConverter()
                    ),
                    CultureInfo.CurrentCulture
                );
        }
    }
}

and then replace the built-in with your custom one in Application_Start:

ValueProviderFactories.Factories.Remove(
    ValueProviderFactories
        .Factories
        .OfType<JsonValueProviderFactory>()
        .FirstOrDefault()
);
ValueProviderFactories.Factories.Add(new JsonDotNetValueProviderFactory());

That's it. Now you are using Json.Net instead of the JavaScriptSerializer for the incoming JSON requests.




回答2:


The modified version:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Web.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace MvcJsonNetTests.Utils
{
    public class JsonNetValueProviderFactory : ValueProviderFactory
    {
        public JsonNetValueProviderFactory()
        {
            Settings = new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Error,
                Converters = { new ExpandoObjectConverter() }
            };
        }

        public JsonSerializerSettings Settings { get; set; }

        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            if (controllerContext == null)
                throw new ArgumentNullException("controllerContext");

            if (controllerContext.HttpContext == null ||
                controllerContext.HttpContext.Request == null ||
                controllerContext.HttpContext.Request.ContentType == null)
            {
                return null;
            }

            if (!controllerContext.HttpContext.Request.ContentType.StartsWith(
                    "application/json", StringComparison.OrdinalIgnoreCase))
            {
                return null;
            }

            if (!controllerContext.HttpContext.Request.IsAjaxRequest())
            {
                return null;
            }

            using (var streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
            {
                using (var jsonReader = new JsonTextReader(streamReader))
                {
                    if (!jsonReader.Read())
                        return null;

                    var jsonSerializer = JsonSerializer.Create(this.Settings);

                    Object jsonObject;
                    switch (jsonReader.TokenType)
                    {
                        case JsonToken.StartArray:
                            jsonObject = jsonSerializer.Deserialize<List<ExpandoObject>>(jsonReader);
                            break;
                        default:
                            jsonObject = jsonSerializer.Deserialize<ExpandoObject>(jsonReader);
                            break;
                    }

                    var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
                    addToBackingStore(backingStore, String.Empty, jsonObject);
                    return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
                }
            }
        }

        private static void addToBackingStore(IDictionary<string, object> backingStore, string prefix, object value)
        {
            var dictionary = value as IDictionary<string, object>;
            if (dictionary != null)
            {
                foreach (var entry in dictionary)
                {
                    addToBackingStore(backingStore, makePropertyKey(prefix, entry.Key), entry.Value);
                }
                return;
            }

            var list = value as IList;
            if (list != null)
            {
                for (var index = 0; index < list.Count; index++)
                {
                    addToBackingStore(backingStore, makeArrayKey(prefix, index), list[index]);
                }
                return;
            }

            backingStore[prefix] = value;
        }

        private static string makeArrayKey(string prefix, int index)
        {
            return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
        }

        private static string makePropertyKey(string prefix, string propertyName)
        {
            return (string.IsNullOrWhiteSpace(prefix)) ? propertyName : prefix + "." + propertyName;
        }
    }
}

Also to register it at the right index:

public static void RegisterFactory()
{
    var defaultJsonFactory = ValueProviderFactories.Factories
        .OfType<JsonValueProviderFactory>().FirstOrDefault();
    var index = ValueProviderFactories.Factories.IndexOf(defaultJsonFactory);
    ValueProviderFactories.Factories.Remove(defaultJsonFactory);
    ValueProviderFactories.Factories.Insert(index, new JsonNetValueProviderFactory());
}


来源:https://stackoverflow.com/questions/11490506/mvc3-controller-with-specific-jsonconverter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!