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
接口的對象序列化以後發生屬性丟失的緣由。