ASP.NET MVC4将带有'ExtraElements'的JSON反序列化到c#ViewModel中

时间:2015-09-24 12:15:08

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

我正在尝试在控制器保存操作上将JSON映射到c#ViewModel。基本上,JSON具有以下结构:

{
   "Id": "",
   "TypeId": "37",
   "FormId": "",
   "ExtraData":{
      "title": "Some random title",
      "contribute": "author",
      "location": {
          "url": "/Files/ed5cf2ea-c920.jpeg",
          "size": "100",
          "format": "application/json"
      }
   }
}

有些字段是“知道”而另一些则不是。 'ExtraData'是所有这些未知字段的容器,它们可以变化很大。

我创建了一个c#VIewModel来映射它:

public class ContentViewModel
{
    public Guid Id { get; set; }
    public int TypeId{ get; set; }
    public int FormId{ get; set; }
    public Dictionary<string, object> ExtraData { get; set; }
}

当我使用上面的JSON发出请求时,只会映射ExtraData的“root”元素。 “location”对象被映射到一个对象。

我试图将Dictionary更改为动态,仍然无法正常工作。我也尝试创建一个JsonConverter,但它从未被执行过。我的猜测它不适用于开箱即用的ASP.NET MVC应用程序?

我尝试了很多东西,搜索过,我觉得我走到了尽头。

编辑:ExtraData将按原样存储在MongoDB中。它可以非常简单或复杂(具有多个节点和嵌套对象)。

2 个答案:

答案 0 :(得分:3)

如果您可以使用“动态”对象解析您的要求(因为您提到过,您已经尝试将字典更改为动态),您可以尝试使用以下方法。

请注意,虽然我不确定这种方法是否符合您的要求,但我希望它可以从某种方式开始帮助您。

我采用了自定义模型绑定器(针对特定属性Type)方法,因为这是处理ASP.NET MVC中复杂模型/属性绑定的另一个可扩展点

自定义模型活页夹

 public class ComplexObjectModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var contentType = controllerContext.HttpContext.Request.ContentType;
            if (!contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
                return (null);

            string jsonString;

            using (var stream = controllerContext.HttpContext.Request.InputStream)
            {
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream))
                    jsonString = reader.ReadToEnd();
            }

            if (string.IsNullOrEmpty(jsonString)) return (null);

            DynamicComplexObject ExtraData = new DynamicComplexObject();

            ExtraData.Details = new JavaScriptSerializer().Deserialize<dynamic>(jsonString);

            return (ExtraData);
        }
    }

在Global.asax中注册自定义活页夹:

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            //Custom model binder registration
            ModelBinders.Binders.Add(typeof(Models.DynamicComplexObject), new ComplexObjectModelBinder());

            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

查看模型:

public class ContentViewModel
{
    public Guid Id { get; set; }
    public int TypeId { get; set; }
    public int FormId { get; set; }
    public DynamicComplexObject ExtraData { get; set; }
}
//You may try to improve the following model and update the custom model binder logic as appropriate.
public class DynamicComplexObject
{
    public dynamic Details { get; set; }
}

在下面的JSON中,我添加了一个用于测试的级别嵌套

var data = {
        "Id": "",
        "TypeId": "37",
        "FormId": "",
        "ExtraData": {
            "title": "Some random title",
            "contribute": "author",
            "location": {
                "url": "/Files/ed5cf2ea-c920.jpeg",
                "size": "100",
                "format": "application/json",
                "onemorelocation": { //Added one more nested object to test
                    "a": 1,
                    "b": 2
                }
            }
        }
    };
$("#btnSubmit").on("click", function () {
        $.ajax({
            "datatype": "json",
            "contentType": "application/json; charset=utf-8",
            "type": "POST",
            "url": '@Url.Action("SaveData", "Home")',
            "data": JSON.stringify(data),
            success: function (d) {
               //Do stuff here
            }
        });
    });

从调试中我可以看到发生的绑定,如图所示。 enter image description here

答案 1 :(得分:0)

Siva的回答可能有效,但我不想搞乱默认的Model Binder。阅读this后,我能理解为什么我的动态/词典没有正确绑定。

所以,我已经创建了一个自定义的ValueProvider,它取代了默认的JsonValueProviderFactory,它不能用于动态的嵌套字典。

<强> JsonNetValueProviderFactory

public sealed class JsonNetValueProviderFactory : 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;

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

<强>的Global.asax.cs

public static void RegisterFactory()
{
        ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
        ValueProviderFactories.Factories.Add(new JsonNetValueProviderFactory());
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
            TypeNameHandling = TypeNameHandling.All
        };

 }

<强>视图模型

public class ContentViewModel
{
   public Guid Id { get; set; }
   public int TypeId{ get; set; }
   public int FormId{ get; set; }
   public dynamic ExtraData { get; set; }
}

同样,Siva的答案也可能有效,但对于我的情况,我认为创建自定义ValueProvider而不是弄乱模型绑定器会更好。

这是有效的: enter image description here

此外,列出了非常有用的链接: ASP.Net MVC3 JSON.Net Custom ValueProviderFactory ASP.NET MVC 3 – Improved JsonValueProviderFactory using Json.Net