• 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?

  • 相关阅读:
    Angular使用$compile为从Ajax加载的HTML绑定ng-click事件
    js获取判断苹果手机机型
    工作中遇到的常见问题
    js实现文字超出部分用省略号代替实例代码
    微信企业号开发之weixin://preInjectJSBridge/fail
    构造函数,super()
    微信企业号开发之 企业号人员身份认证与开发
    微信企业号开发之回调模式的接口开发
    微信公众号开发的开发环境要求和准备工作
    react-router 中的history(react中关于后退键的处理用的到)
  • 原文地址:https://www.cnblogs.com/songxingzheng/p/6482431.html
Copyright © 2020-2023  润新知