zoukankan      html  css  js  c++  java
  • 在MVC中使用Json.Net序列化和反序列化Json对象

    在.Net的MVC开发中,经常会使用到Json对象,于是,系统提供了JsonResult这个对象,其本质是调用.Net系统自带的Json序列化类JavaScriptSerializer对数据对象进行序列化。但是这个系统自带的Json序列化对象方法没有Json.Net好用,于是打算有些时候用Json.Net替代默认的实现。

    要实现有时候用Json.Net,有时候用默认实现,那么就要保证系统中两种实现并存。对于Server将对象序列化成Json传给Client很简单,我们只需要建立一个新的ActionResult,我们命名为JsonNetResult,然后在Get时,return这个JsonNetResult即可。JsonNetResult的代码实现为:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MvcJsonNet
    {
        using System.IO;
        using System.Web;
        using System.Web.Mvc;
        using Newtonsoft.Json;
    
        public class JsonNetResult : JsonResult
        {
            public JsonNetResult()
            {
                Settings = new JsonSerializerSettings
                {
                    ReferenceLoopHandling = ReferenceLoopHandling.Error
                };
            }
            public JsonNetResult(object data, JsonRequestBehavior behavior = JsonRequestBehavior.AllowGet, string contentType=null, Encoding contentEncoding=null)
            {
                this.Data = data;
                this.JsonRequestBehavior = behavior;
                this.ContentEncoding = contentEncoding;
                this.ContentType = contentType;
            }
    
            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());
                }
            }
        }
    }

    要返回一个Json.Net序号列后的对象,那么调用方法是:

    [HttpGet]
    public ActionResult GetJsonNet()
    {
        var myClass = InitClass();
        return new JsonNetResult(myClass);
    }

    这是Get方法,但是对于ClientPost一个Json回Server,那么就比较麻烦了,需要修改好几处地方:

    1,建立Json.Net的ValueProviderFactory,这个类主要就是用于Json字符串的反序列化。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace MvcJsonNet
    {
        using System.Collections;
        using System.Dynamic;
        using System.Globalization;
        using System.IO;
        using System.Web.Mvc;
        using System.Web.Script.Serialization;
        using Newtonsoft.Json;
    
        public class JsonNetValueProviderFactory : ValueProviderFactory
        {
            private void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
            {
                IDictionary<string, object> d = value as IDictionary<string, object>;
                if (d != null)
                {
                    foreach (KeyValuePair<string, object> entry in d)
                    {
                        AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
                    }
                    return;
                }
    
                IList l = value as IList;
                if (l != null)
                {
                    for (int i = 0; i < l.Count; i++)
                    {
                        AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
                    }
                    return;
                }
    
                // primitive
                backingStore[prefix] = value;
            }
    
            private object GetDeserializedObject(ControllerContext controllerContext)
            {
                if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.InvariantCultureIgnoreCase))
                {
                    // not JSON request
                    return null;
                }
    
                StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
                string bodyText = reader.ReadToEnd();
                if (String.IsNullOrEmpty(bodyText))
                {
                    // no JSON data
                    return null;
                }
                //接下来的代码是关键,判断content type,如果是json.net,那么就使用Json.Net的反序列化方法,如果不是,那么就使用系统默认的反序列化方法
                if (controllerContext.HttpContext.Request.ContentType.StartsWith("application/json.net", StringComparison.InvariantCultureIgnoreCase))
                {
                    var jsonData = JsonConvert.DeserializeObject<ExpandoObject>(bodyText);
                    return jsonData;
                }
                else
                {
                    JavaScriptSerializer serializer = new JavaScriptSerializer();
                    object jsonData = serializer.DeserializeObject(bodyText);
                    return jsonData;
                }
            }
    
            public override IValueProvider GetValueProvider(ControllerContext controllerContext)
            {
                if (controllerContext == null)
                {
                    throw new ArgumentNullException("controllerContext");
                }
    
                object jsonData = GetDeserializedObject(controllerContext);
                if (jsonData == null)
                {
                    return null;
                }
    
                Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
                AddToBackingStore(backingStore, String.Empty, jsonData);
                return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
            }
    
            private  string MakeArrayKey(string prefix, int index)
            {
                return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
            }
    
            private  string MakePropertyKey(string prefix, string propertyName)
            {
                return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
            }
        }
    }

    2,在初始化MVC时替换掉默认的JsonValueProviderFactory。
    在Global.asax的Application_Start时,写入以下代码:

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

    3,建立新的ModelBinder,命名为JsonNetModelBinder。

    namespace MvcJsonNet
    {
        using System;
        using System.ComponentModel;
        using System.Diagnostics;
        using System.Globalization;
        using System.Linq;
        using System.Web.Mvc;
        using Newtonsoft.Json;
    
        public class JsonNetModelBinder : DefaultModelBinder
        {
            protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext,
                                                 PropertyDescriptor propertyDescriptor)
            {
                Debug.WriteLine("BindProperty");
                if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json.net",
                                                                                  StringComparison
                                                                                      .InvariantCultureIgnoreCase))
                {
                    //根据Content type来判断,只有json.net这种content type的才会使用该ModelBinder,否则使用默认的Binder
                    base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
                    return;
                }
    
                // need to skip properties that aren't part of the request, else we might hit a StackOverflowException
                string name = propertyDescriptor.Name;
                foreach (object attribute in propertyDescriptor.Attributes)
                {
                    if (attribute is JsonPropertyAttribute)
                    {
                        var jp = attribute as JsonPropertyAttribute;
                        name = jp.PropertyName;
                    }
                }
    
                string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, name);
                if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey))
                {
                    return;
                }
    
                // call into the property's model binder
                IModelBinder propertyBinder = Binders.GetBinder(propertyDescriptor.PropertyType);
                object originalPropertyValue = propertyDescriptor.GetValue(bindingContext.Model);
                ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
                propertyMetadata.Model = originalPropertyValue;
                var innerBindingContext = new ModelBindingContext
                    {
                        ModelMetadata = propertyMetadata,
                        ModelName = fullPropertyKey,
                        ModelState = bindingContext.ModelState,
                        ValueProvider = bindingContext.ValueProvider
                    };
                object newPropertyValue = GetPropertyValue(controllerContext, innerBindingContext, propertyDescriptor,
                                                           propertyBinder);
                propertyMetadata.Model = newPropertyValue;
    
                // validation
                ModelState modelState = bindingContext.ModelState[fullPropertyKey];
                if (modelState == null || modelState.Errors.Count == 0)
                {
                    if (OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, newPropertyValue))
                    {
                        SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
                        OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
                    }
                }
                else
                {
                    SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
    
                    // Convert FormatExceptions (type conversion failures) into InvalidValue messages
                    foreach (
                        ModelError error in
                            modelState.Errors.Where(err => String.IsNullOrEmpty(err.ErrorMessage) && err.Exception != null)
                                      .ToList())
                    {
                        for (Exception exception = error.Exception; exception != null; exception = exception.InnerException)
                        {
                            if (exception is FormatException)
                            {
                                string displayName = propertyMetadata.GetDisplayName();
                                string errorMessageTemplate = "The value '{0}' is not valid for {1}.";
                                string errorMessage = String.Format(CultureInfo.CurrentCulture, errorMessageTemplate,
                                                                    modelState.Value.AttemptedValue, displayName);
                                modelState.Errors.Remove(error);
                                modelState.Errors.Add(errorMessage);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    4,建立一个VModel的基类,为该基类添加Attribute,然后在Global中添加Model和Binder的映射。

    [ModelBinder(typeof (JsonNetModelBinder))]
    public abstract class VEntity
    {
        public virtual long Id { get; set; }
    }

    Global.asax中Application_Start添加代码:

     ModelBinders.Binders.Add(typeof(VEntity), new JsonNetModelBinder());

    5在前端Post Json时,指定content type为application/json.net

     function PostJsonNet() {
                 var jsonstr = $("#jsonstring")[0].innerHTML;
                 $.ajax({
                     url: "MyTest/CreateFromJsonNet",
                     type: "POST",
                     data: jsonstr,
                     contentType: "application/json.net",
                     dataType: "json",
                     success: function (data) {
                         alert(data);
                      
                     }
                 });
             }

    我们这样处理后,Client在往Server传送Json数据时,如果指定了contentType是application/json,那么就使用系统默认的方法来反序列化对象,如果是application/json.net,那么就使用Json.Net来反序列化。

    示例程序下载

  • 相关阅读:
    hosts.deny hosts.allow防止ssh暴力破解密码
    linux查看当前ssh登陆的ip
    ubuntu下如何查看用户登录及系统授权相关信息
    ubuntu查看日志中访问次数前十的ip
    magento清空日志表
    mysql general error 2006
    mysql 1205 lock wait timeout exceeded
    mysql 504 timeout问题
    PHP : 数据库中int类型保存时间并通过年月份时分秒进行显示
    PHP : 封装Mysqli的连接、关闭和增改查(面向过程)
  • 原文地址:https://www.cnblogs.com/studyzy/p/mvc_and_json_dot_net.html
Copyright © 2011-2022 走看看