2011年10月27日 星期四

ASP.NET MVC + JSON 自定義JsonResult 1


在「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 1」裡面有提到,

如果把EF取出的物件不做任何處理然後在JsonResult中直接使用 Json()方法傳回,會出現「循環參考」的錯誤,

而文章中有提到說可以使用Json.NET所提供的 JsonConvert.SerializeObject()方法來回傳Json資料,

但是必須要使用ActionResult的類別來回傳,而且為了讓Content-Type正確,也需要指定Content-Type,如以下的程式:

public ActionResult Product(int? id)
{
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    return Content(JsonConvert.SerializeObject(jo), "application/json");
  }
  else
  {
    ProductService service = new ProductService();
    var product = service.Single(id.Value);
    return Content(JsonConvert.SerializeObject(product), "application/json");
  }
}

使用了Json.NET來做為序列化JSON資料的工具,而Json.NET的官網上面也有提供了一個JsonNetResult的類別,

這樣一來在Action就不用再多一個動作去指定使用JsonConvert.Serialize() 方法來序列化資料,

另外也不用使用回傳類別ActionResult,也不用再去指定Content-Type。


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

http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx

JsonNetResult類別:

public class JsonNetResult : ActionResult
 
{
 
  public Encoding ContentEncoding { get; set; }
 
  public string ContentType { get; set; }
 
  public object Data { get; set; }
 
 
 
  public JsonSerializerSettings SerializerSettings { get; set; }
 
  public Formatting Formatting { get; set; }
 
 
 
  public JsonNetResult()
 
  {
 
    SerializerSettings = new JsonSerializerSettings();
 
  }
 
 
 
  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";
 
 
 
    if (ContentEncoding != null)
 
      response.ContentEncoding = ContentEncoding;
 
 
 
    if (Data != null)
 
    {
 
      JsonTextWriter writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
 
 
 
      JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
 
      serializer.Serialize(writer, Data);
 
 
 
      writer.Flush();
 
    }
 
  }
 
}
 

 

使用的方式如下:

public JsonNetResult Product(int? id)
{
  JsonNetResult jsonNetResult = new JsonNetResult();
  jsonNetResult.Formatting = Newtonsoft.Json.Formatting.None;
  jsonNetResult.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
  if (!id.HasValue)
  {
    Dictionary<string, string> jo = new Dictionary<string, string>();
    jo.Add("Msg", "請輸入產品ID編號.");
    jsonNetResult.Data = jo;
  }
  else
  {
    ProductService service = new ProductService();
    var product = service.Single(id.Value);
    var result = new 
    {
      ProductID = product.ProductID,
      ProductName = product.ProductName,
      CategoryID = product.CategoryID,
      CategoryName = product.Category.CategoryName,
      UnitPrice = product.UnitPrice,
      UnitsInStock = product.UnitsInStock,
      UnitsOnOrder = product.UnitsOnOrder
    };
    jsonNetResult.Data = result;
  }
  return jsonNetResult;
}

首先建立一個JsonNetResult的物件,接著指定 Formatting 與 SerializerSettings.NullValueHandling,

Formatting

JSON written by the serializer with an option of Formatting.Indented produces nicely formatted, easy to read JSON – great when you are developing. Formatting.None on the other hand keeps the JSON result small, skipping all unnecessary spaces and line breaks to produce the most compact and efficient JSON possible.

NullValueHandling

NullValueHandling is an option on the JsonSerializer and controls how the serializer handles properties with a null value. By setting a value of NullValueHandling.Ignore the JsonSerializer skips writing any properties that have a value of null.

接著就是把要序列化的物件資料給放到 jsonNetResult.Data 中,最後再 return jsonNetResult; 就可以了。

 

執行結果:

image

來看看上述的Formatting的設定差異,

Formatting.None

這個設定並不會把JSON資料做內縮的格式化,所以序列化的JSON資料大小會比較小。

image

Formatting.Indented

這個設定則會把JSON資料做內縮的格式化,所以序列化的JSON資料大小會比較大。

image

 

上面所序列化的資料並不是一開始從EF取出來的Product物件,而是另外建立一個匿名物件,

而這物件的成員則是從取出的Product物件裡而來的,

為什麼這樣做呢?

雖然使用Json.NET的JsonConvert.SerializeObject()方法並不會有循環錯誤的狀況發生,

但因為EF取出物件資料,預設是會把有關連的資料與資料集也一同的取出,

所以如果把Product物件就直接放到jsonNetResult.Data中,則會出現序列化資料過大的情形。

image

所以必須說 JsonNetResult 會比較適合用在單純的物件資料上,

如果物件的成員是有包含關連的資料,那麼 JsonNetResult 就不是一個好的選擇。

 

而在「使用Entity Framework 將物件轉為JSON時遇到循環參考錯誤 3」這篇文章裡,

我們有自定義了兩個JavaScriptConvert類別:「EFSimpleJavaScriptConvert」與「EFJavaScriptConvert」

所以我們也可以來做一個專門回應 Jsons內容的JsonResult,這個就有待下一篇文章來做詳細的說明。

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。