Newtonsoft.Json 序列化踩坑之 IEnumerable
Newtonsoft.Json 序列化踩坑之 IEnumerable
Intro
Newtonsoft.Json 是 .NET 下最受欢迎 JSON 操作库,使用起来也是非常方便,有时候也可能会不小心就踩坑了,这次就踩了一个,坑是这样的,如果要序列化的对象实现了 IEnumerable 接口,Newtonsoft.Json 就会认为这个对象是一个数组。。然后遍历这个对象,输出其中的值,如果是一个自定义的类型而且还有其他属性,其他属性就会被忽略,序列化之后就会发生数据丢失。
问题代码
在我的公用类库 WeihanLi.Common 有一个分页列表的Model:
在 1.0.21及之前版本是这样定义的 源码
using System;
using System.Collections;
using System.Collections.Generic;
namespace WeihanLi.Common.Models
{
/// <summary>
/// IPagedListModel
/// </summary>
/// <typeparam name="T">Type</typeparam>
public interface IPagedListModel<out T> : IReadOnlyList<T>
{
/// <summary>
/// Data
/// </summary>
IReadOnlyList<T> Data { get; }
/// <summary>
/// PageNumber
/// </summary>
int PageNumber { get; }
/// <summary>
/// PageSize
/// </summary>
int PageSize { get; }
/// <summary>
/// TotalDataCount
/// </summary>
int TotalCount { get; set; }
}
/// <inheritdoc />
/// <summary>
/// 分页Model
/// </summary>
/// <typeparam name="T">Type</typeparam>
[Serializable]
public class PagedListModel<T> : IPagedListModel<T>
{
public IReadOnlyList<T> Data { get; set; }
private int _pageNumber = 1;
public int PageNumber
{
get => _pageNumber;
set
{
if (value > 0)
{
_pageNumber = value;
}
}
}
private int _pageSize = 10;
public int PageSize
{
get => _pageSize;
set
{
if (value > 0)
{
_pageSize = value;
}
}
}
private int _totalCount;
public int TotalCount
{
get => _totalCount;
set
{
if (value > 0)
{
_totalCount = value;
}
}
}
public int PageCount => Convert.ToInt32(Math.Ceiling(_totalCount * 1.0 / _pageSize));
public IEnumerator<T> GetEnumerator()
{
return Data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Data.GetEnumerator();
}
public T this[int index] => Data[index];
public int Count => Data.Count;
}
}
上面的这种定义相当于实现了 IEnumerable 接口,之所以实现这个接口,是因为可以直接遍历这个对象,不需要遍历这个对象的Data 属性上遍历,但是这样序列化的时候就会有问题, PageNumber/PageSize/TotalPage 之类的信息序列化时就会丢失
Solution
不要实现 IEnumerable 接口就可以了,修改后的代码如下所示:
using System;
using System.Collections.Generic;
namespace WeihanLi.Common.Models
{
/// <summary>
/// IPagedListModel
/// </summary>
/// <typeparam name="T">Type</typeparam>
public interface IPagedListModel<out T>
{
/// <summary>
/// Data
/// </summary>
IReadOnlyList<T> Data { get; }
/// <summary>
/// PageNumber
/// </summary>
int PageNumber { get; }
/// <summary>
/// PageSize
/// </summary>
int PageSize { get; }
/// <summary>
/// TotalDataCount
/// </summary>
int TotalCount { get; set; }
}
/// <inheritdoc />
/// <summary>
/// 分页Model
/// </summary>
/// <typeparam name="T">Type</typeparam>
[Serializable]
public class PagedListModel<T> : IPagedListModel<T>
{
public IReadOnlyList<T> Data { get; set; }
private int _pageNumber = 1;
public int PageNumber
{
get => _pageNumber;
set
{
if (value > 0)
{
_pageNumber = value;
}
}
}
private int _pageSize = 10;
public int PageSize
{
get => _pageSize;
set
{
if (value > 0)
{
_pageSize = value;
}
}
}
private int _totalCount;
public int TotalCount
{
get => _totalCount;
set
{
if (value > 0)
{
_totalCount = value;
}
}
}
public int PageCount => Convert.ToInt32(Math.Ceiling(_totalCount * 1.0 / _pageSize));
public T this[int index] => Data[index];
public int Count => Data.Count;
}
}
Test
写个示例测试一下,原来的代码类型改为 PagedListModel1 ,测试代码如下:
PagedListModel1:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace DotNetCoreSample.Test
{
public class PagedListModel1<T> : IEnumerable<T>
{
public IReadOnlyList<T> Data { get; set; }
private int _pageNumber = 1;
public int PageNumber
{
get => _pageNumber;
set
{
if (value > 0)
{
_pageNumber = value;
}
}
}
private int _pageSize = 10;
public int PageSize
{
get => _pageSize;
set
{
if (value > 0)
{
_pageSize = value;
}
}
}
private int _totalCount;
public int TotalCount
{
get => _totalCount;
set
{
if (value > 0)
{
_totalCount = value;
}
}
}
public int PageCount => Convert.ToInt32(Math.Ceiling(_totalCount * 1.0 / _pageSize));
public T this[int index] => Data[index];
public int Count => Data.Count;
public IEnumerator<T> GetEnumerator()
{
return Data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Data.GetEnumerator();
}
}
}
测试代码:
var pagedListModel = new PagedListModel<int>()
{
PageNumber = 2, PageSize = 2, TotalCount = 6, Data = new int[] {1, 2},
};
var pagedListModel1 = new PagedListModel1<int>()
{
PageNumber = 2,
PageSize = 2,
TotalCount = 6,
Data = new int[] { 1, 2 },
};
Console.WriteLine($"pagedListModel:{JsonConvert.SerializeObject(pagedListModel)}, pagedListModel1:{JsonConvert.SerializeObject(pagedListModel1)}");
output:
pagedListModel:{"Data":[1,2],"PageNumber":2,"PageSize":2,"TotalCount":6,"PageCount":3,"Count":2}, pagedListModel1:[1,2]
可以看到实现了 IEnumerable 接口的那个类序列化之后一些属性丢失了
Research
查看 Newtonsoft.Json 源码 https://github.com/JamesNK/Newtonsoft.Json
,找到为什么实现了 IEnumerable 接口就会有问题,最后找到了这里 https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs#L1218

可以看到只要实现了 IEnumerable 接口,就会被当作是一个Json 数组,foreach 遍历其中的元素,其他属性就会被忽略掉了,这就是为什么上面我们实现了 IEnumerable 接口的对象序列化之后发生属性丢失的原因。
Reference
- https://github.com/JamesNK/Newtonsoft.Json
- https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs#L1218
- https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/DotNetCoreSample/Program.cs
Newtonsoft.Json 序列化踩坑之 IEnumerable的更多相关文章
- [C#][Newtonsoft.Json] Newtonsoft.Json 序列化时的一些其它用法
Newtonsoft.Json 序列化时的一些其它用法 在进行序列化时我们一般会选择使用匿名类型 new { },或者添加一个新类(包含想输出的所有字段).但不可避免的会出现以下情形:如属性值隐藏(敏 ...
- C# 使用Newtonsoft.Json序列化自定义类型
Json.Net是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单.通过Linq To JSON可以快速的读写Json,通过JsonSerializ ...
- c# 使用 Newtonsoft.Json 序列化json字符串以及,反序列化对象
1. 序列化 对象 /** 使用 Newtonsoft.Json 序列化对象 **/ [WebMethod] public String getPersonInfos() { // 初始化数据 Lis ...
- Newtonsoft.Json 序列化和反序列化 以及时间格式 2 高级使用
手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数 ...
- Newtonsoft.Json序列化日期时间去T的几种方式。
原文地址:MVC web api 返回JSON的几种方式,Newtonsoft.Json序列化日期时间去T的几种方式. http://www.cnblogs.com/wuball/p/4231343. ...
- MVC web api 返回JSON的几种方式,Newtonsoft.Json序列化日期时间去T的几种方式。
原文链接:https://www.muhanxue.com/essays/2015/01/8623699.html MVC web api 返回JSON的几种方式 1.在WebApiConfig的Re ...
- Newtonsoft.Json 序列化和反序列化 时间格式【转】
1.JSON序列化 string JsonStr= JsonConvert.SerializeObject(Entity); eg: A a=new A(); a.Name="Elain ...
- newtonsoft.json 序列化,反序列化
public class Book { public string BookID { get; set; } public DateTime PublishDate { get; set; } pub ...
- Newtonsoft.Json 序列化和反序列化 时间格式
From : http://www.cnblogs.com/litian/p/3870975.html 1.JSON序列化 string JsonStr= JsonConvert.SerializeO ...
随机推荐
- 在项目中常用的JS方法封装
使用方法简单,只需要放在你的 utils.js 工具文件中,直接export const 加上下面封装方法,在别的文件中使用 {方法1,方法2,方法3...}引用后直接使用即可. 01.输入一个值.返 ...
- Office批量打印助手(Excel 批量打印、Word 批量打印)
最新版本:1.0.6664.34636(更新日期:2018年3月31日) 下载地址:点击下载 程序简介: 本程序能批量打印 Word 文件.Excel 工作簿. 使用程序前请先安装 .NET Fra ...
- Java连载58-静态内部类、成员内部类详解
一.内部类的分类: (1)静态内部类(2)成员内部类(3)局部内部类(4)匿名内部类 二.静态内部类 1.静态内部类可以等同的看做是静态变量 内部类的重要作用:可以访问外部类中的私有数据 2.静态内部 ...
- 从数据表字段 float 和 double 说起
今天在公司讨论项目重构的问题时,公司的 DBA 针对表中的字段大概介绍了一下 float 和 double 的存储方式.然后,我发现这个问题又回到了浮点数类型在内存中的存储方式,即 IEEE 对浮点数 ...
- easyui treegrid数据重复加载问题
在使用easyui的时候,出现了数据重复加载的问题.如下图 关于这个问题有两种说法,第一种说法是 easyui-datagrid 类在html和js中重复定义,数据渲染时会加载两次.另一种是$(&qu ...
- linux创建用户并锁定用户目录和首次登陆强制修改密码
1. 创建用户及访问目录 mkdir -p /home/user/testuser 创建用户目录 useradd testuser -d /home/user/testuser -M ...
- Web前端基础(9):JavaScript(三)
1. 常用内置对象 所谓内置对象就是ECMAScript提供出来的一些对象,我们知道对象都是有相应的属性和方法. 1.1 数组Array 1.1.1 数组的创建方式 字面量方式创建(推荐大家使用这种方 ...
- cesium 入门开发系列地图鹰眼功能(附源码下载)
前言 cesium 入门开发系列环境知识点了解:cesium api文档介绍,详细介绍 cesium 每个类的函数以及属性等等cesium 在线例子 内容概览 cesium 结合 leaflet 实现 ...
- 从华为“鸿蒙”备胎看IT项目建设
别误会啊,本文并不在讲大家在做IT项目建设的时候学华为做一个备胎系统,以防正主系统崩掉之后能够及时替换到备胎系统里面,不影响业务. 前段时间华为被美帝制裁,然后各家组织对华为各种限制.然而华为整体布局 ...
- 大话Git系列之初识版本控制系统(2)
本次接着上次的来介绍版本控制系统,这个讲到的将是Git与Github,说一下什么是Git,什么是GitHub?他们之间的关系是什么? 1.Git 是代码管理的工具 2.GitHub 是基于Git实现的 ...