Newtonsoft.Json
序列化踩坑之 IEnumerable
Newtonsoft.Json
是 .NET 下最受欢迎 JSON 操做库,使用起来也是很是方便,有时候也可能会不当心就踩坑了,此次就踩了一个,坑是这样的,若是要序列化的对象实现了 IEnumerable
接口,Newtonsoft.Json
就会认为这个对象是一个数组。。而后遍历这个对象,输出其中的值,若是是一个自定义的类型并且还有其余属性,其余属性就会被忽略,序列化以后就会发生数据丢失。git
在个人公用类库 WeihanLi.Common 有一个分页列表的Model:github
在 1.0.21及以前版本是这样定义的 源码json
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 之类的信息序列化时就会丢失数组
不要实现 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; } }
写个示例测试一下,原来的代码类型改成 PagedListModel1 ,测试代码以下:this
PagedListModel1:spa
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(); } } }
测试代码:code
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 接口的那个类序列化以后一些属性丢失了blog
查看 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
接口的对象序列化以后发生属性丢失的缘由。