在MVC中自定义Json.Net序列化和反序列化Json对象

前端之家收集整理的这篇文章主要介绍了在MVC中自定义Json.Net序列化和反序列化Json对象前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

要实现有时候用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,newPropertyValue))
                {
                    SetProperty(controllerContext,newPropertyValue);
                    OnPropertyValidated(controllerContext,newPropertyValue);
                }
            }
            else
            {
                SetProperty(controllerContext,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来反序列化。

猜你在找的Json相关文章