在.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());
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);
}
});
}
【本文章出自部落格園深藍居,轉載請注明作者出處,如果您覺得部落客的文章對您有很大幫助。】