ASP.NET WebAPI (反)序列化用[SerializableAttribute]修飾的類的一個坑


發現問題

在 ASP.NET WebAPI 項目中,有這樣的 ViewModel 類:

[Serializable]
class Product
{
    public int Id { get; set; }
    public decimal Price { get; set; }
    public DateTime ProductDate { get; set; }
}

Controller 和 Action 代碼如下:

public class ProductController : ApiController
{
    public Product Get(int id)
    {
        return new Product()
        {
            Id = 1,
            Price = 12.9m,
            ProductDate = new DateTime(1992, 1, 1)
        };
    }
}

客戶端請求該資源: http://localhost:5000/api/product/1,結果發現 WebAPI 返回這樣的JSON,如下:

{
  "<Id>k__BackingField": 1,
  "<Price>k__BackingField": 12.9,
  "<ProductDate>k__BackingField": "1992-01-01T00:00:00"
}

我們知道,自動屬性雖然沒有定義字段,但是C#編譯器會生成相應的私有字段,類似 private int <Id>k__BackingField
我們期望 WebAPI 序列化時將屬性名作為 JSON 的鍵,而這里 WebAPI 序列化的卻是編譯器生成的私有字段,顯然不符合我們的要求。

奇怪的地方是,如果單獨用 Json.NET 類庫去序列化,則能得到期望的 JSON,如下:

{
  "Id": 1,
  "Price": 12.9,
  "ProductDate": "1992-01-01T00:00:00"
}

找到原因

經過 Google 一番,原來是和 SerializableAttribute 有關。
從 Json.NET 4.5 Release 2 版本開始,新增這樣的特性:

如果檢測到類型有 SerializableAttribute,將序列化該類型的所有私有/公開字段,並且忽略其屬性。
如果不想要這個新特性,可以對類應用 JsonObjectAttribute 來覆蓋,或者在全局范圍內 設置 DefaultContractResolverIgnoreSerializableAttributetrue
而從 release 3 版本開始 IgnoreSerializableAttribute 默認為 true

而 ASP.NET WebAPI 依賴 Json.NET,但是卻將 IgnoreSerializableAttribute 設置為 false,也就是不忽略 SerializableAttribute,導致如果類用 [SerializableAttribute] 修飾,就只(反)序列化字段而忽略屬性,於是當用自動屬性時,輸出的就是編譯器自動生成的字段名。

解決問題

了解到問題的原因后,可以通過以下方式解決:

  1. 去掉 [Serializable]
  2. 應用 [JsonObjectAttribute]
  3. 設置 IgnoreSerializableAttribute

最簡單的方法就是去掉 [Serializable],如果由於某些原因不能去除,可以用其他兩種辦法。

應用 JsonObjectAttribute

[Newtonsoft.Json.JsonObject]
[System.Serializable]
class Product
{
    public int Id { get; set; }
    public decimal Price { get; set; }
    public DateTime ProductDate { get; set; }
}

設置 IgnoreSerializableAttribute

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // 其他代碼省略 .........

        // 將 SerializerSettings 重置為默認值 IgnoreSerializableAttribute = true
        config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings();
    }
}

參考:
Json.NET 4.5 Release 2 – Serializable support and bug fixes
Why won't Web API deserialize this but JSON.Net will?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM