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. Hibernate session.save()实体类,主键增长问题

    实体类如下: package com.wondersgroup.test.entity;   import java.io.Serializable;   import javax.persisten ...

  2. Springcloud 微服务 高并发(实战1):第1版秒杀

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列之15 [博客园总入口 ] 前言 前言 疯狂创客圈(笔者尼恩创建的高并发研习社群)Springcloud 高并发系列文章,将为大家介绍三个版 ...

  3. SQLServer之数据库表转化为实体类【带注释】

    1.在开发过程中,有时候需要将数据库表转化为实体类.手敲除了不方便,还容易出错.本着DRY+懒人原则,参考了一位老司机的博客[见底部],并在其基础上进行了优化.[原先是不带注释的] DECLARE @ ...

  4. npm与cnpm的区别

    NPM(Node Package Manager,节点包管理器)是NodeJS的包管理器,用于节点插件的管理(包括安装,卸载和管理依赖等).NPM是随同新版的NodeJS一起安装的包管理工具,所以我们 ...

  5. 呵呵,asp.net 屁东西我都会忘记

    ASP.NET中的好些基本的东西有的忘了,有的需要学,从现在开始,给自己开一个基本的常用知识点的总结,一是学习,二是备忘. 今天算是第一篇吧! DropDownList在从数据库中得到数据源绑定后,计 ...

  6. Java操作数据库——在JDBC里使用事务

    Java操作数据库——在JDBC里使用事务 摘要:本文主要学习了如何在JDBC里使用事务. 使用Connection的事务控制方法 当JDBC程序向数据库获得一个Connection对象时,默认情况下 ...

  7. javaWeb核心技术第九篇之JSP

    JSP:全名是Java Server Pages,它是建立在Servlet规范之上的动态网页开发技术.在JSP文件中,HTML代码与Java代码共同存在,其中,HTML代码用来实现网页中静态内容的显示 ...

  8. js的try catch使用心得

      1 try catch的使用,永远应该放在你的控制范围之内,而不应该防范未知的错误.也就是说你很清楚知道这里是有可能”出错“的,而且你很清楚知道什么前提下会出错,你就是要故意利用报错信息来区分错误 ...

  9. Command CompileSwiftSources failed with a nonzero exit code

    Xcode错误提示:Command CompileSwiftSources failed with a nonzero exit code,网上找了好多才搞定,通过在Build Setting里面自添 ...

  10. 20180918 begin

    20180918-20190717 风 雅 颂(305,每天一首): 诗经鉴赏, 180918-1030 魔鬼经济学 <唐宋词十七讲>叶嘉莹<最美的宋词> 布谷鸟<诗境浅 ...