ASP.NET MVC小改裝 - 以Json.NET取代JavaScriptSerializer

转自:http://blog.darkthread.net/post-2012-08-30-asp-net-mvc-and-json-net.aspx

前篇文章中,我們實測驗證ASP.NET MVC 4的JsonResult採用JavaScriptSerializer進行JSON序列化,然而若要100%確認,其實還有更精準的做法--因為ASP.NET MVC 4是一個Open Source專案,Use the source, Luke!

由CodePlex取得ASP.NET MVC的原始程式碼,追蹤Controller.Json()可以查到以下這段,Json()會在內部建立了一個新的JsonResult物件傳回:

排版顯示純文字

protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)

{

return new JsonResult

{

Data = data,

ContentType = contentType,

ContentEncoding = contentEncoding,

JsonRequestBehavior = behavior

};

}

而深入JsonResult類別,可以在ExecuteResult()查出它是使用JavaScriptSerializer無誤!!

排版顯示純文字

public override void ExecuteResult(ControllerContext context)

{

if (context == null)

{

throw new ArgumentNullException("context");

}

if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&

String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))

{

throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);

}

 

HttpResponseBase response = context.HttpContext.Response;

 

if (!String.IsNullOrEmpty(ContentType))

{

response.ContentType = ContentType;

}

else

{

response.ContentType = "application/json";

}

if (ContentEncoding != null)

{

response.ContentEncoding = ContentEncoding;

}

if (Data != null)

{

JavaScriptSerializer serializer = new JavaScriptSerializer();

if (MaxJsonLength.HasValue)

{

serializer.MaxJsonLength = MaxJsonLength.Value;

}

if (RecursionLimit.HasValue)

{

serializer.RecursionLimit = RecursionLimit.Value;

}

response.Write(serializer.Serialize(Data));

}

}

Json.NET的作者針對ASP.NET MVC的JSON序列化寫過JsonNetResult,原則上在Controller中用它取代JsonResult就可在MVC中改以Json.NET進行序列化,但Controller.Json()預設還是會使用JavaScriptSerializer。在追蹤原始碼的過程,我留意到Controller.Json()被宣告成virtual,換句話說,只要繼承Controller建立客製版Controller類別,覆寫(Override) Json()方法改傳JsonNetResult,就能改變Controller行為,原程式不需修改就能將JavaScriptSerializer抽換成Json.NET!

以下是一個簡單的Controller改裝範例--JsonNetController: 
(由於JsonRequestBehavior.DenyGet判斷的部分會用到只有System.Web.Mvc內部才能存取的錯誤息訊文字資源,此處取巧,在發生DenyGet情境時抛回JsonResult觸發原有錯誤)

排版顯示純文字複製文字

以前篇文章的BooController為例,我們將繼承來源由Controller改為JsonNetController,原有的Json(DateTime)寫法不需修改,但輸出結果就會改為Json.NET的版本:

排版顯示純文字

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Web;

using System.Web.Mvc;

using System.Web.Script.Serialization;

using Newtonsoft.Json;

 

namespace MyMvc.Controllers

{

public class BooController : JsonNetController

{

public ActionResult TestJson()

{

return Json(DateTime.Now, JsonRequestBehavior.AllowGet);

}

 

public ActionResult TestJsonNotAllowGet()

{

return Json(DateTime.Now);

}

 

public ActionResult DateJsonWtf()

{

WebClient wc = new WebClient();

Uri url = Request.Url;

string s = wc.DownloadString(

string.Format("http://{0}:{1}/Boo/TestJson",

url.Host, url.Port));

JavaScriptSerializer jss = new JavaScriptSerializer();

DateTime d = jss.Deserialize<DateTime>(s);

return Content(string.Format(

"Now={0:MM-dd HH:mm}, Json={2}, Restored DateTime={1:MM-dd HH:mm}",

DateTime.Now, d, s));

}

 

public ActionResult TestJsonNetResult()

{

return new JsonNetResult()

{

Data = DateTime.Now

};

}

}

}

測試JsonRequestBehavior.DenyGet的情境,可得到與JsonResult相同的錯誤訊息: 

Json(DateTime.Now)傳回結果則已變成Json.NET的ISO 8601格式: 

測試在C#端以JavaScriptSerializer還原TestJson的傳回結果,資料正確無時區差異。另外,此一結果也驗證了JavaScriptSerilalizer能正確地將"2012-08-30T05:04:38.1348135+08:00"的ISO 8601格式反序列化回DateTime。

JavaScriptSerializer在先前的效能測試中,慘敗給Json.NET及DataContractJsonSerializer,而其獨有的DateTime編碼格式,在JavaScript等異質Client端需要額外處理,缺少時區資訊衍生的轉換地雷更是惱人,歸納下來,和IE6一樣,己成該從地表消失的禍水~ 對於新建專案,若無相容性的包袱,建議一律改用Json.NET,方為王道。

你可能感兴趣的:(mvc,json)