什么问题?

NewtonSoft.Json是我们最常用的Json组件库之一了。这里来讨论下使用NewtonSoft.Json序列化List<T>子类的情景。序列化使用了类JsonSerializer

情景重现

如果我们有一个Field实体类。另有一个FieldGroup类表示Field的分组,并携带组属性GroupFormat。我们需要序列化这个FieldGroup,该如何实现呢?

机智如我,这么写了:

// FieldGroup 实现类
public class FieldGroup : List<Field>
{
public Format GroupFormat{ get;set; }
} // 序列化过程
public void main()
{
var group = new FieldGroup()
{
GroupFormat = "Format 1"
};
group.Add(new Field()
{
Name = "Field 1"
}); Console.WriteLine(JsonUtil.SerializeByNsj(group));
}

结果我很纳闷儿,GroupFormat属性被JsonSerializer吃了吗?

[
{
"Name": "Field 1"
}
]

咋解决呢?

既然JsonSerializer不会自己处理这个GroupFormat属性,那我来告诉你它是必须要序列化的!我们使用JsonObject(MemberSerialization.OptOut)来标记这个类除了显示地标记了JsonIgnore特性的公有属性都需要被序列化。

[JsonObject(MemberSerialization.OptOut)]
public class FieldGroup : List<Field>
{
public string Format { get; set; }
}

这下好了吧?emmmmmmm......

{
"Format": "Format 1",
"Capacity": 4,
"Count": 1
}

!!!又把List吃了吗?

原因分析

这点也是在StackOverflow上看到的。Json数组本身就是纯数组,并不能在数组上应用一个属性。

我的解决方法

既然Json不能支持带属性的数组,那只能在代码里面通过索引模拟一个类似于集合的类了。

[JsonObject(MemberSerialization.OptOut)]
public class FieldGroup : IEnumerable<Field>
{
public PrintFormat GroupFormat { get; set; } = new PrintFormat();
// 使用内部的 List<Field> 代替继承,可直接被序列化和反序列化
public List<Field> Fields { get; set; } = new List<Field>();
// 使用索引对外提供类似于List<T>的访问方式;
public Field this[int index]
{
get => Fields[index];
set => Fields[index] = value;
}
// 提供List<T>一致的Add方法,有需要可以提供其他方法
public void Add(Field field)
{
Fields.Add(field);
}
// 提供类似于List<T>的IEnumerable功能
public IEnumerator<Field> GetEnumerator()
{
return Fields.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

这次的结果是?

{
"Fields": [
{
"Name": "Field 1"
}
],
"Format": "Format 1"
}

老外有话说

Is there any way to JSON.NET-serialize a subclass of List that also has extra properties?

public class LocationListJsonConverter : JsonConverter
{
public override bool CanConvert(System.Type objectType)
{
return objectType == typeof(LocationList);
} public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
{
var locationList = (existingValue as LocationList) ?? new LocationList();
var jLocationList = JObject.ReadFrom(reader); locationList.IsExpanded = (bool)(jLocationList["IsExpanded"] ?? false); var jLocations = jLocationList["_Items"];
if(jLocations != null)
{
foreach(var jLocation in jLocations)
{
var location = serializer.Deserialize<Location>(new JTokenReader(jLocation));
locationList.Add(location);
}
}
return locationList;
} public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var locationList = value as LocationList;
JObject jLocationList = new JObject();
if(locationList.IsExpanded)
jLocationList.Add("IsExpanded", true);
if(locationList.Count > 0)
{
var jLocations = new JArray();
foreach(var location in locationList)
{
jLocations.Add(JObject.FromObject(location, serializer));
}
jLocationList.Add("_Items", jLocations);
}
jLocationList.WriteTo(writer);
}
}

继承自List<T>的类通过NewtonJson的序列化问题的更多相关文章

  1. 【转载】 C++多继承中重写不同基类中相同原型的虚函数

    本篇随笔为转载,原文地址:C++多继承中重写不同基类中相同原型的虚函数. 在C++多继承体系当中,在派生类中可以重写不同基类中的虚函数.下面就是一个例子: class CBaseA { public: ...

  2. 自定义继承于Page的基类

    自定义继承于Page的基类:MyBasePage[校验用户是否登录,如果登录则获取用户信息,否则跳转到登录页面]============================================ ...

  3. 面向对象编程(九)——面向对象三大特性之继承以及重写、Object类的介绍

    面向对象三大特性 面向对象三大特征:继承 :封装/隐藏 :多态(为了适应需求的多种变化,使代码变得更加通用!) 封装:主要实现了隐藏细节,对用户提供访问接口,无需关心方法的具体实现. 继承:很好的实现 ...

  4. 从零开始学C++之继承(二):继承与构造函数、派生类到基类的转换

    一.不能自动继承的成员函数 构造函数 析构函数 =运算符 二.继承与构造函数 基类的构造函数不被继承,派生类中需要声明自己的构造函数. 声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类 ...

  5. python之继承、抽象类、新式类和经典类

    一.上节补充1.静态属性静态属性 : 类的属性,所有的对象共享这个变量 如果用对象名去修改类的静态属性:在对象的空间中又创建了一个属性,而不能修改类中属性的值 操作静态属性应该用类名来操作 例1:请你 ...

  6. 第四百零六节,自定义用户表类来继承Django的用户表类,

    第四百零六节,自定义用户表类来继承Django的用户表类, models.py from django.db import models # Create your models here. from ...

  7. Python类总结-继承-子类和父类,新式类和经典类

    子类和父类 class Father(object): #子类在使用super调用父类时,Father后面要加object --新式类 def __init__(self): self.Fname = ...

  8. C++ 类的继承六(多继承的二义性--虚基类)

    //多继承的二义性--虚基类(了解为主) #include<iostream> using namespace std; /* 多继承在现在的项目开发中一般不使用,他会增加项目的复杂度 * ...

  9. 谈谈java中静态变量与静态方法在有继承关系的两个类中调用

    谈谈java中静态变量与静态方法在有继承关系的两个类中调用 学习的中如果遇到不明白或者不清楚的的时候,就是自己做些测试,自己去试试,这次我就做一个关于静态变量和静态方法在有继承关系的两个类中的问题测试 ...

随机推荐

  1. Ice框架介绍

    概述 Ice是一个开源的综合性RPC框架,以高性能和原生支持微服务的架构而著称.提供了很多可以直接使用的组件,如注册中心IceGrid,部署工具IcePatch2,防火墙穿透Glacier2,发布订阅 ...

  2. Canal:同步mysql增量数据工具,一篇详解核心知识点

    老刘是一名即将找工作的研二学生,写博客一方面是总结大数据开发的知识点,一方面是希望能够帮助伙伴让自学从此不求人.由于老刘是自学大数据开发,博客中肯定会存在一些不足,还希望大家能够批评指正,让我们一起进 ...

  3. typora+PicGo+gitee搭建免费的的床

    一.gitee 1.第一步拥有自己的gitee账号 没有的可以自己去注册gitee地址 2.使用自己的gitee账号创建仓库 创建好之后注意 记住.com/以后的地址 此处就为y***L/photo- ...

  4. 如何在K8s,Docker-Compose注入镜像Tag

    最近在做基于容器的CI/CD, 一个朴素的自动部署的思路是: 从Git Repo打出git tag,作为镜像Tag ssh远程登录到部署机器 向部署环境注入镜像Tag,拉取镜像,重新部署 下面分享我是 ...

  5. LVS负载均衡之DR模式原理介绍

    LVS基本原理 流程解释: 当用户向负载均衡调度器(Director Server)发起请求,调度器将请求发往至内核空间 PREROUTING 链首先会接收到用户请求,判断目标 IP 确定是本机 IP ...

  6. 多路复用器Select、Poll、Epoll区别梳理

    注意:本文是本人的学习总结,可能存在理解上的错误,请带着怀疑眼光看待,如果有不准确的地方欢迎指出,疑义相与析.为了叙述完整性,前面有一些前置知识,可以根据目录直接看后面的详解部分. 前置知识 用户态与 ...

  7. java.io.IOException: Could not find resource com/xxx/xxxMapper.xml

    java.io.IOException: Could not find resource com/xxx/xxxMapper.xml 报错内容: org.apache.ibatis.exception ...

  8. fiddler安装以及使用说明

    一.fiddler fiddler是一个抓包工具,通过使用它抓包我们可以很清晰的看到抓的内容的协议,URL,参数等. 1.安装 在普通下载网站找到安装包,直接安装,点击下一步即可. 二.使用 1.se ...

  9. JPEG解码——(3)文件头解析

    与具体的编码数据空间相比,jpeg文件头占据非常小乃至可以忽略不计的大小. 仍然拿JPEG解码--(1)JPEG文件格式概览中的<animal park>这张图片来举例,从跳过SOS(FF ...

  10. MSSQL 注入笔记

    前置知识: 登录名:登录sql server服务器的用户,而不是操作"数据库用户名". 固定服务器角色:就是上面登录名所属的权限组.其中重要的就是"sysadmin&qu ...