網頁

2011年10月27日 星期四

ASP.NET MVC + JSON 自定義JsonResult 2


接續「ASP.NET MVC + JSON 自定義JsonResult 1」的內容,

上一篇文章的最後有說,Json.NET所提供的 JsonNetResult類別並不是很適合拿來使用,

而在「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 3」裡面,我們也建立了兩個JavaScriptConverter類別,

所以我們就自己來建立一個自己定義的JsonResult,並且使用之前所建立EFJavaScriptSerializer,

這個 EFJavaScriptSerializer 可以分別依據狀況使用 EFSimpleJavaScriptConverter 或 EFJavaScriptConverter…


廢話不多說,直接來看做好的 CustomJsonResult 類別

using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace Test.Helper
{
  public class CustomJsonResult : JsonResult
  {
    public new Encoding ContentEncoding { get; set; }
    public int? MaxDepth { get; set; }
    public CustomJsonResult()
    {
      JsonRequestBehavior = JsonRequestBehavior.DenyGet;
    }
    public override void ExecuteResult(ControllerContext context)
    {
      if (context == null)
      {
        throw new ArgumentNullException("context");
      }
      HttpResponseBase response = context.HttpContext.Response;
      response.ContentType = !String.IsNullOrEmpty(ContentType) 
        ? ContentType 
        : "application/json";
      response.ContentEncoding = this.ContentEncoding == null
        ? System.Text.Encoding.UTF8
        : this.ContentEncoding;
      if (Data != null)
      {
        EFJavaScriptSerializer serializer = !this.MaxDepth.HasValue
          ? new EFJavaScriptSerializer()
          : new EFJavaScriptSerializer(this.MaxDepth.Value);
        
        response.Write(serializer.Serialize(Data));
      }
    }
  }
}

這個類別繼承 JsonResult,這個 CustomJsonResult 比原本的JsonResult 還多了一個 MaxDeprth 屬性,

這是可以在做序列化時,使用 EFJavaScriptConverter 去轉換資料到多少的深度,

預設的Content-Type為「application/json

預設的Encoding為「System.Text.Encoding.UTF8

最後建立的EFJavaScriptSerializer則會根據MaxDepth的數值來決定是要用哪一個 JavaScriptConverter。

 

EFJavaScriptSerializer類別

當沒有指定MaxDepth數值時,會使用EFSimpleJavaScriptConverter來做序列化,只會轉換物件本身的欄位,不包含關連資料。

當有指定MaxDepth時,則使用 EFJavaScriptConverter來做序列化,且依據深度來包含轉換多少深度的關連資料。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
 
namespace Test.Helper
{
    public class EFJavaScriptSerializer : JavaScriptSerializer
    {
        public EFJavaScriptSerializer()
        {
            RegisterConverters(new List<JavaScriptConverter> { new EFSimpleJavaScriptConverter() });
        }
 
        public EFJavaScriptSerializer(int maxDepth = 1, EFJavaScriptConverter parent = null)
        {
            if (maxDepth < 1)
            {
                RegisterConverters(new List<JavaScriptConverter> { new EFSimpleJavaScriptConverter() });
            }
            else
            {
                RegisterConverters(new List<JavaScriptConverter> { new EFJavaScriptConverter(maxDepth, parent) });
            }
        }
    }
}

 

使用情境一:

序列化一個從EF取出的Product物件,不指定深度,所以這個只會序列化Product物件自己的欄位資料。

public JsonResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Json(jo);
  }
  else
  {
    ProductService service = new ProductService();
    var product = service.Single(id.Value);
    return new CustomJsonResult
    {
      Data = product,
      JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
  }
}

執行結果:

image

 

使用情境二:

序列化CategoryID為1的Product集合,不指定深度。

public JsonResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Json(jo);
  }
  else
  {
    ProductService service = new ProductService();
    var products = service.FindBy(id.Value);
    return new CustomJsonResult
    {
      Data = products,
      JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
  }
}

執行結果:

image

image

 

使用情境三:

序列化一個從EF取出的Product物件,指定深度為1,所以會序列化Product物件自己的欄位資料以及關連的物件。

public JsonResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Json(jo);
  }
  else
  {
    ProductService service = new ProductService();
    var product = service.Single(id.Value);
    return new CustomJsonResult
    {
      Data = product,
      MaxDepth = 1,
      JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
  }
}

執行結果:

image

 

使用情境四:

序列化CategoryID為1的Product集合,指定深度為1。

public JsonResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Json(jo);
  }
  else
  {
    ProductService service = new ProductService();
    var products = service.FindBy(id.Value);
    return new CustomJsonResult
    {
      Data = products,
      MaxDepth = 1,
      JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
  }
}

執行結果:

image

image

 

在Controller中使用JsonResult的Action,在序列化物件的時候就可以使用CustomJsonResult類別。

 

如果說,每次使用JsonResult的Action於最後的回傳不想用 new CustomJsonResult{ … } 的方式,

可以另外建立一個BaseController類別,然後在這個BaseController類別中去建立CustomJson()方法

 

BaseController類別:

using System.Web.Mvc;
using Test.Helper;
namespace Test.Web
{
  public class BaseController : Controller
  {
    protected internal JsonResult CustomJson(object data)
    {
      return new CustomJsonResult { Data = data };
    }
    protected internal JsonResult CustomJson(object data, JsonRequestBehavior jsonRequestBehavior = JsonRequestBehavior.DenyGet)
    {
      return new CustomJsonResult { Data = data, JsonRequestBehavior = jsonRequestBehavior };
    }
    protected internal JsonResult CustomJson(object data, int maxDepth = 1)
    {
      return new CustomJsonResult { Data = data, MaxDepth = maxDepth };
    }
    protected internal JsonResult CustomJson(object data, int maxDepth = 1, JsonRequestBehavior jsonRequestBehavior = JsonRequestBehavior.DenyGet)
    {
      return new CustomJsonResult { Data = data, MaxDepth = maxDepth, JsonRequestBehavior = jsonRequestBehavior };
    }
  }
}

而修改後的Action:

沒有指定深度

public JsonResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Json(jo);
  }
  else
  {
    ProductService service = new ProductService();
    var products = service.FindBy(id.Value);
    //return new CustomJsonResult
    //{
    //    Data = products,
    //    JsonRequestBehavior = JsonRequestBehavior.AllowGet
    //};
    return CustomJson(products, JsonRequestBehavior.AllowGet);
  }
}

指定深度

public JsonResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Json(jo);
  }
  else
  {
    ProductService service = new ProductService();
    var products = service.FindBy(id.Value);
    //return new CustomJsonResult
    //{
    //    Data = products,
    //    MaxDepth = 1,
    //    JsonRequestBehavior = JsonRequestBehavior.AllowGet
    //};
    return CustomJson(products, 1, JsonRequestBehavior.AllowGet);
  }
}

這樣在return時就可以使用CustomJson(),不必使用 return new CustomJsonResult{ … }。

 

延伸閱讀:

ASP.NET MVC + JSON 自定義JsonResult 1

使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 3

 

參考連結:

Json.NET - ASP.NET MVC and Json.NET:JsonNetResult

Stackoverflow - Using JSON.net as default JSON serializer in ASP.NET MVC 3 - Is it possible?

Stackoverflow - Change Default JSON Serializer Used In ASP MVC3

 

以上

1 則留言: