在MVC中使用Json.Net序列化和反序列化Json对象

来源:互联网 发布:淘宝评价服务怎么说 编辑:程序博客网 时间:2024/06/01 01:34

在.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来反序列化。

示例程序下载

原创粉丝点击