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

Newtonsoft.Json 序列化踩坑之 IEnumerable的更多相关文章

  1. [C#][Newtonsoft.Json] Newtonsoft.Json 序列化时的一些其它用法

    Newtonsoft.Json 序列化时的一些其它用法 在进行序列化时我们一般会选择使用匿名类型 new { },或者添加一个新类(包含想输出的所有字段).但不可避免的会出现以下情形:如属性值隐藏(敏 ...

  2. C# 使用Newtonsoft.Json序列化自定义类型

    Json.Net是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单.通过Linq To JSON可以快速的读写Json,通过JsonSerializ ...

  3. c# 使用 Newtonsoft.Json 序列化json字符串以及,反序列化对象

    1. 序列化 对象 /** 使用 Newtonsoft.Json 序列化对象 **/ [WebMethod] public String getPersonInfos() { // 初始化数据 Lis ...

  4. Newtonsoft.Json 序列化和反序列化 以及时间格式 2 高级使用

    手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数 ...

  5. Newtonsoft.Json序列化日期时间去T的几种方式。

    原文地址:MVC web api 返回JSON的几种方式,Newtonsoft.Json序列化日期时间去T的几种方式. http://www.cnblogs.com/wuball/p/4231343. ...

  6. MVC web api 返回JSON的几种方式,Newtonsoft.Json序列化日期时间去T的几种方式。

    原文链接:https://www.muhanxue.com/essays/2015/01/8623699.html MVC web api 返回JSON的几种方式 1.在WebApiConfig的Re ...

  7. Newtonsoft.Json 序列化和反序列化 时间格式【转】

    1.JSON序列化 string JsonStr= JsonConvert.SerializeObject(Entity); eg:   A a=new A(); a.Name="Elain ...

  8. newtonsoft.json 序列化,反序列化

    public class Book { public string BookID { get; set; } public DateTime PublishDate { get; set; } pub ...

  9. Newtonsoft.Json 序列化和反序列化 时间格式

    From : http://www.cnblogs.com/litian/p/3870975.html 1.JSON序列化 string JsonStr= JsonConvert.SerializeO ...

随机推荐

  1. BZOJ3894/LG4313 文理分科 新建点最小割

    问题描述 BZOJ3894 LG4313 题解 显然一个人只能选文/理 -> 一个人只能属于文(S).理(T)集合中的一个 可以把选择文得到 \(art\) 的收益看做选择文失去 \(scien ...

  2. mysql 事务四要素杂谈

    事务四要素 对于数据库来说,并发性和准确性是数据库需要权衡的两个点. 类似于我们的应用系统,又要要性能还要要准确. 数据准确性这一条来说,最好的控制就是串行化,都别急,一个一个来.这样数据就没问题了. ...

  3. Java面试题和解答(五)

    1.在Java中Executor和Executors的区别? Executor是线程池的顶层接口,它的实现类如下图所示: Executors是一个类,提供了多个静态方法,用于生成不同类型的线程池,如下 ...

  4. 解决 vscode 中 nuget 插件无法获取包版本的问题

    解决 vscode 中 nuget 插件无法获取包版本的问题 1.问题描述 大概在今年的7月份左右,我忽然发现 NuGet Package Manager 拓展没法正常使用了,只能查询到包: 选完包之 ...

  5. PAT 1010 Radix 进制转换+二分法

    Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The an ...

  6. java架构之路(Sharding JDBC)mysql5.7yum安装和主从

    安装mysql5.7单机 1.获取安装yum包 [root@iZm5e7sz135n16ua2rmbk6Z local]# wget http://dev.mysql.com/get/mysql57- ...

  7. Selenium(八):其他操作元素的方法、冻结界面、弹出对话框、开发技巧

    1. 其他操作元素的方法 之前我们对web元素做的操作主要是:选择元素,然后点击元素或者输入字符串. 还有没有其他的操作了呢?有. 比如:比如鼠标右键点击.双击.移动鼠标到某个元素.鼠标拖拽等. 这些 ...

  8. Angular ElementRef详解

    一.为什么要用ElementRef Angular 的口号是 - "一套框架,多种平台.同时适用手机与桌面 (One framework.Mobile & desktop.)&quo ...

  9. 团队展示&选题

    团队展示 1.队名:螺旋升天队 2.队员学号: 李光证 3117004660 (队长) 卢俊杰 3117004662 吴子昊 3117004671 陈浩民 3117004646 陈俊铭 3117004 ...

  10. 工作日志,证书无效 unable to find valid certification path to requested target

    工作日志,证书无效 unable to find valid certification path to requested target 最近被这个问题弄得头大.导致所有用到 se.transmod ...