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
下一篇: 我怎么没有想到啊?
推荐阅读
-
vue踩坑记录之数组定义和赋值问题
-
Angular 4.x+Ionic3踩坑之Ionic 3.x界面传值详解
-
Angular 4.x+Ionic3踩坑之Ionic3.x pop反向传值详解
-
Element-UI踩坑之Pagination组件的使用
-
SpringBoot1.x升级SpringBoot2.x踩坑之文件上传大小限制
-
Python踩坑系列之安装pycrypto报错:“Microsoft Visual C++14.0 is required”问题。
-
Newtonsoft.Json 序列化踩坑之 IEnumerable
-
ReactNative踩坑之配置调试端口的解决方法
-
Html5踩坑记之mandMobile使用小记
-
微信小程序开发篇之踩坑记录