什么问题?

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. JDBC入门程序总结

    JDBC本质 只是一个接口 每个数据库的规范 就是实现类的接口 其实是官方 定义的一套操作所有关系型数据库的规则,就是接口,各个数据库厂商去实现这套接口,提供数据库驱动jar包, 我们可以使用这套接口 ...

  2. bat批处理积累

    1 ::所有命令不回显,包含echo off自身也不回显 2 @echo off 3 4 ::rem或双冒号都为注释行 5 6 rem 变量赋值,注意变量和等号之间不能有空格,等号后的空格会作为变量值 ...

  3. Ubuntu Terminal命令行新建仓库并推送到远程仓库

    通常情况下,在本地新建一个仓库之后,需要在远端网页端也新建一个空的同名仓库,然后将两者进行关联才能推送. 那有没有办法直接在命令行就完成从新建到推送的过程而不需要中间在网页端也操作一番呢?办法当然是有 ...

  4. linux下的命令自动补齐增强

    linux 7 下 安装 bash-completion 可以实现命令的参数的自动补齐

  5. 简易双色球dome分享

    代码如下: <style type="text/css"> div {font-weight: bold;text-align: center;} .tone{widt ...

  6. CentOS安装mysql、JDK、Tomcat部署环境

    1.1. 安装mysql-5.6 1.1.1. 检测系统内部有没有安装其他的mysql数据库 $ rpm -qa | grep mysql 1.1.2. 如果内部有需要先删除Mysql $ yum r ...

  7. linux系统层面调优

    linux系统层面调优和常见的面试题 - 云+社区 - 腾讯云 https://cloud.tencent.com/developer/article/1664287

  8. asctime_s asctime

    asctime_s  asctime // rand随机数.cpp : 此文件包含 "main" 函数.程序执行将在此处开始并结束. // #include "pch.h ...

  9. springboot配置rabbitmq

    一.消息生成者 1.1消息生成者配置 1.2 消息发送端代码 1.3 创建交换机,队列,并建立关系 二.消费者 2.1消费者 三.限流配置 3.1配置文件 #在单个请求中处理的消息个数,他应该大于等于 ...

  10. luoguP6754 [BalticOI 2013 Day1] Palindrome-Free Numbers

    目录 luoguP6754 [BalticOI 2013 Day1] Palindrome-Free Numbers 简述题意: Solution: Code luoguP6754 [BalticOI ...