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

ContractResolver.CreateContract

可以看到只要实现了 IEnumerable 接口,就会被当作是一个Json 数组,foreach 遍历其中的元素,其他属性就会被忽略掉了,这就是为什么上面我们实现了 IEnumerable 接口的对象序列化之后发生属性丢失的原因。

Reference

转载于:https://www.cnblogs.com/weihanli/p/11081483.html

在 VB.NET 中使用 Newtonsoft.Json 序列化结构体的过程与序列化类的过程类似。你可以使用 `JsonConvert.SerializeObject` 方法将结构体对象转换为 JSON 字符串,以及使用 `JsonConvert.DeserializeObject` 方法将 JSON 字符串转换回结构体对象。 以下是一个示例代码,演示如何使用 Newtonsoft.Json 序列化结构体: ```vb Imports Newtonsoft.Json ' 定义一个结构体 Public Structure Person Public Name As String Public Age As Integer End Structure Sub Main() ' 创建一个结构体对象 Dim person As New Person With { .Name = "John", .Age = 25 } ' 序列化结构体为 JSON 字符串 Dim json As String = JsonConvert.SerializeObject(person) ' 输出序列化后的 JSON 字符串 Console.WriteLine(json) ' 反序列化 JSON 字符串为结构体对象 Dim deserializedPerson As Person = JsonConvert.DeserializeObject(Of Person)(json) ' 输出反序列化后的结构体对象的属性值 Console.WriteLine(deserializedPerson.Name) Console.WriteLine(deserializedPerson.Age) End Sub ``` 在这个示例中,我们首先定义了一个名为 `Person` 的结构体,其中包含了 `Name` 和 `Age` 两个属性。 然后,我们创建了一个 `Person` 结构体对象,并设置了其属性值。 接下来,使用 `JsonConvert.SerializeObject` 方法将结构体对象序列化JSON 字符串,并将结果存储在 `json` 变量中。 然后,我们输出序列化后的 JSON 字符串。 最后,使用 `JsonConvert.DeserializeObject` 方法将 JSON 字符串反序列化为 `Person` 结构体对象,并将结果存储在 `deserializedPerson` 变量中。 最后,我们输出反序列化后的结构体对象的属性值。 确保在项目中引用了 Newtonsoft.Json 库,并在代码文件中导入了命名空间 `Newtonsoft.Json`。 希望这个示例能够帮助你在 VB.NET 中使用 Newtonsoft.Json 序列化结构体。如果你还有其他问题,请随时提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值