什么问题?

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. (十三)利用BASE_DIR来import模板

    实际工程的组织架构一般是这样的: bin包下的bin.py是实际的执行文件,my_mould包下的是业务逻辑的实现模板 bin.py需要import my_mould下的py文件,而bin和my_mo ...

  2. kubernets之namespace

    一 命名空间的介绍以及作用 1  概念 为了方便不同部门之间对kubernets集群的使用,并且对其进行有效的隔离,kubernets提供了一种资源隔离手段,通过将各种不同资源分组到 一个区域,并且统 ...

  3. CodeMonke少儿编程第1章 step与turn

    第1章 step与turn 目标 了解游戏舞台的各组成部分 掌握step和turn指令的用法 说起计算机,对于不了解它的人来说,也许会感到有些神秘,其实不然,它不过是能够接收指令并且按照指令执行的一种 ...

  4. 《进击吧!Blazor!》第一章 1.初识 Blazor

    作者介绍 陈超超 Ant Design Blazor 项目贡献者 拥有十多年从业经验,长期基于.Net技术栈进行架构与开发产品的工作,Ant Design Blazor 项目贡献者,现就职于正泰集团 ...

  5. 解决MyBatis-Plus 3.3.1中自动生成代码tinyint(1)无法自动转换为Boolean 的办法

    解决方法 1.在测试类中新建一个类MySqlTypeConvertCustom,继承MySqlTypeConvert并实现ITypeConvert后覆盖processTypeConvert方法. 2. ...

  6. 网络可视化工具netron详细安装流程

    1.netron 简介 在实际的项目中,经过会遇到各种网络模型,需要我们快速去了解网络结构.如果单纯的去看模型文件,脑海中很难直观的浮现网络的架构. 这时,就可以使用netron可视化工具,可以清晰的 ...

  7. es_python_操作

    获取es索引 https://www.itranslater.com/qa/details/2583886977221264384

  8. Spring Security 实战干货:分布式对象SharedObject

    1. 前言 在上一篇我们对AuthenticationManager的初始化的细节进行了分析,其中里面有一段代码引起了不少同学的注意: ApplicationContext context = htt ...

  9. XV6学习(2)Lab syscall

    实验的代码放在了Github上. 第二个实验是Lab: system calls. 这个实验主要就是自己实现几个简单的系统调用并添加到XV6中. XV6系统调用 添加系统调用主要有以下几步: 在use ...

  10. C#高级编程第11版 - 第二章 索引

    [1]2.1.1 Hello,World! 1. using static System.Console; // ... WriteLine("Hello World!"); 提前 ...