【问题标题】:MVC3 Controller with specific JsonConverter具有特定 JsonConverter 的 MVC3 控制器
【发布时间】:2012-07-15 08:24:41
【问题描述】:

设置如下:

我有一些旨在供 jQuery ajax 请求使用的 MVC 控制器。一个正常的请求看起来有点像这样:

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

我的控制器如下所示:

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

这对传递给控制器​​的大多数对象都非常有效,除了在这种特定情况下,传递的对象的属性之一需要以特定方式反序列化。

我添加了一个 JsonConverter,我之前使用过 MVC4 Web API,但在这种情况下,我需要将它应用到常规的 mvc 控制器。

我尝试像这样在 global.asax 中注册 JsonConverter:

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

但到目前为止还不能反序列化对象。

【问题讨论】:

    标签: asp.net-mvc json serialization


    【解决方案1】:

    如果您想在将 JSON 请求绑定到视图模型时使用 Json.NET,则应将内置的 JsonValueProviderFactory 类替换为自定义类。

    你可以写一个如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
                    );
            }
        }
    }
    

    然后用您在Application_Start 中的自定义替换内置的:

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

    就是这样。现在您使用 Json.Net 而不是 JavaScriptSerializer 来处理传入的 JSON 请求。

    【讨论】:

    • 是的,谢谢达林。我已经在这样做了,它对大多数对象都很好。具有“GrizlyString”属性的对象除外。我需要找到一种方法来指定使用自定义 JsonConverter。
    • 您可以将自定义转换器作为构造函数参数传递给我展示的JsonDotNetValueProviderFactory
    • 我尝试了这样的反序列化: var deserializeObject = JsonConvert.DeserializeObject(bodyText, new ExpandoObjectConverter(), new GrizlyStringConverter() );
    • 在进一步检查/调试后,对象会正确反序列化为 ExpandoObject,但无法转换为所需的 ViewModel。当调用到达我的控制器时,它甚至没有为参数传递 null,它只是试图无参数地调用控制器方法。
    • 没关系,问题出在对象构造器级别。谢谢你的回答,真的很有帮助。
    【解决方案2】:

    修改后的版本:

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

    还要在正确的索引处注册:

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

    【讨论】:

      猜你喜欢
      • 2014-09-07
      • 1970-01-01
      • 2012-12-29
      • 2014-08-24
      • 1970-01-01
      • 2016-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多