发现问题

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

ASP.NET WebAPI (反)序列化用[SerializableAttribute]修饰的类的一个坑的更多相关文章

  1. 细说Asp.Net WebAPI消息处理管道

    我们在开发完Asp.Net WebAPI程序后,可以使用WebHost寄宿方式或者SelfHost寄宿方式来部署Asp.Net WebAPI.所谓WebHost寄宿就是通过Asp.Net来实现:所谓S ...

  2. ASP.NET WebAPI使用Swagger生成测试文档

    ASP.NET WebAPI使用Swagger生成测试文档 SwaggerUI是一个简单的Restful API测试和文档工具.简单.漂亮.易用(官方demo).通过读取JSON配置显示API .项目 ...

  3. ASP.NET WebAPI 测试文档 (Swagger)

    ASP.NET WebAPI使用Swagger生成测试文档 SwaggerUI是一个简单的Restful API测试和文档工具.简单.漂亮.易用(官方demo).通过读取JSON配置显示API .项目 ...

  4. 笔记-ASP.NET WebApi

    本文是针对ASP.NET WepApi 2 的笔记. Web API 可返回的结果: 1.void 2.HttpResponseMessage 3.IHttpActionResult 4.其他类型 返 ...

  5. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  6. 【开源】分享一个前后端分离方案-前端angularjs+requirejs+dhtmlx 后端asp.net webapi

    一.前言 半年前左右折腾了一个前后端分离的架子,这几天才想起来翻出来分享给大家.关于前后端分离这个话题大家也谈了很久了,希望我这个实践能对大家有点点帮助,演示和源码都贴在后面. 二.技术架构 这两年a ...

  7. 前端angularjs+requirejs+dhtmlx 后端asp.net webapi

    享一个前后端分离方案源码-前端angularjs+requirejs+dhtmlx 后端asp.net webapi   一.前言 半年前左右折腾了一个前后端分离的架子,这几天才想起来翻出来分享给大家 ...

  8. Asp.Net WebApi核心对象解析(二)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  9. [翻译] ASP.NET WebAPI 中的异常处理

    原文链接:https://docs.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling 本文介绍 ...

随机推荐

  1. 企业官网Web原型制作分享-Tesla

    Tesla是汽车行业知名的奢华品牌,产品为纯电动汽车,知名度极高.此模板正是取自Tesla的官网,高端大图配上文字排版,彰显了汽车的奢华感觉. 本原型由国产Mockplus(原型工具)和iDoc(智能 ...

  2. samtools flagstat

    samtools flagstat命令简介: 统计输入文件的相关数据并将这些数据输出至屏幕显示.每一项统计数据都由两部分组成,分别是QC pass和QC failed,表示通过QC的reads数据量和 ...

  3. ListView动态改变每一项的高度。

    ListView中每一项的高度默认是相同的,除非超过其预定高度值,否则需要动点手脚. VariableSizedListView 继承 ListView然后重写protected override v ...

  4. 如何将spring boot项目打包成war包

    一.修改打包形式 在pom.xml里设置 <packaging>war</packaging> 二.移除嵌入式tomcat插件 在pom.xml里找到spring-boot-s ...

  5. mysql的一些配置优化

    [mysqld]lower_case_table_names=1datadir=/var/lib/mysqlsocket=/var/lib/mysql/mysql.sockuser=mysql# Di ...

  6. 2018.12.15 spoj Substrings(后缀自动机)

    传送门 后缀自动机基础题. 求长度为iii的子串出现次数的最大值. 对原串建出samsamsam,然后用sizsizsiz更新每个maxlenmaxlenmaxlen的答案. 然后由于后缀链接将其转化 ...

  7. 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)

    传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ...

  8. Educational Codeforces Round 62 E 局部dp + 定义状态取消后效性

    https://codeforces.com/contest/1140/problem/E 局部dp + 定义状态取消后效性 题意 给你一个某些位置可以改变的字符串,假如字符串存在回文子串,那么这个字 ...

  9. 安装SourceTree遇到的一个个坑

    之前在公司的电脑上满心欢喜的安装了下,很顺利就成功了,回来在自己电脑上安装,结果坑不能停,以此来纪念下吧! 下载完成后,进行安装: 这里我是申请了个账户,选第一个user an existing ac ...

  10. location位置操作

    使用location对象可以通过很多方式来改变浏览器的位置. location.assign('http://www.klkx.com') 传入一个URL地址 这样可以立即打开一个新的URL并在浏览器 ...