序列化与反序列化

本文更多的描述是将对象序列化为文本,更具体一步说是将实体类对象序列化为json字符串。

什么是序列化和反序列化?

序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程。也就是说序列化与反序列化是一个互逆的过程。

序列化 ( seriallization ):将对象转化为便于传输的格式, 常见的序列化格式:二进制格式,字节数组,json字符串,xml字符串。

反序列化 ( deseriallization ):将序列化的数据恢复为对象。

为什么需要序列化?

在我们编写的程序当中,在程序运行时我们会在代码里创建很多的 对象对象 里存有相应的程序运行时需要用到的数据,这些数据在程序退出后或者是对象释放后就会彻底丢失,因为这些数据是存在于内存当中的,但我们想要的是在程序退出后的下一次运行时我们还需要用这些数据,我们该怎么办,解决方法之一就是序列化对象之后将数据进行磁盘上的保存。

在 C# 语言当中序列化与反序列化 - Json文本

注:我们平常说 Json 通常讲的是 Json 字符串,它是一种人类可读的文本。计算机是不可以直接可读出格式文本里面的数据的。

在 JavaScript 当中它拥有一类型对象,叫 JSON 对象,他可以直接操作 Json 格式的文本,文本与对象不要混为一谈。

我们常用的方式是使用 Newtonsoft.Json 这个 nuget 包,可在vs当中使用 nuget 管理工具安装此包(甚至在项目中由于引用了很多的第三方库,而第三方库中极有可能已经引用了此包,我们都无需安装)。

首先创建一个简单的实体类

1
2
3
4
5
6
7
8
class Student
{
public string Name { get; set; }

public int Age { get; set; }

public string Gender { get; set; }
}

将对象序列化为json文本

1
2
3
4
5
6
7
8
// 首先引入 Newtonsoft.Json 命名空间
using Newtonsoft.Json;
...
Student student = new Student { Name = "Job", Age = 28, Gender = "男" };
string studentText = JsonConvert.SerializeObject(student);
Console.WriteLine(studentText);
// 这样我们就得到了被序列化后的文本 studentText
// 输出: {"Name":"Job","Age":28,"Gender":"男"}

将文本反序列化为Student对象

1
2
3
Student studentDe = JsonConvert.DeserializeObject<Student>(studentText);
Console.WriteLine($@"姓名:{studentDe.Name} 年龄:{studentDe.Age} 性别:{studentDe.Gender}");
// 输出: 姓名:Job 年龄:28 性别:男

稍复杂一点点的一个序列化与反序列化

首先建一个后端经常响应的数据格式实体

1
2
3
4
5
6
7
8
class ResponseData
{
public int Code { get; set; }

public string Message { get; set; }

public string Data { get; set; }
}

序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Student> students = new List<Student>
{
new Student { Name = "Job", Age = 28, Gender = "男" },
new Student { Name = "Jordan", Age = 30, Gender = "男" },
new Student { Name = "Steve", Age = 27, Gender = "男" }
};
string studentsText = JsonConvert.SerializeObject(students);
ResponseData responseData = new ResponseData
{
Code = 1,
Message = "Hello 序列化与反序列化",
Data = studentsText
};
string responseDataText = JsonConvert.SerializeObject(responseData);
Console.WriteLine(responseDataText);

// 输出:{"Code":1,"Message":"Hello 序列化与反序列化","Data":"[{\"Name\":\"Job\",\"Age\":28,\"Gender\":\"男\"},{\"Name\":\"Jordan\",\"Age\":30,\"Gender\":\"男\"},{\"Name\":\"Steve\",\"Age\":27,\"Gender\":\"男\"}]"}

你会发现输出的时候key为”Data”的value值,他不是我们正常的Json文本格式,他加了很多的 \ (反斜杠),

反序列化

1
2
3
4
5
6
7
8
9
10
ResponseData responseDataDe = JsonConvert.DeserializeObject<ResponseData>(responseDataText);
List<Student> studentsDe = JsonConvert.DeserializeObject<List<Student>>(responseDataDe.Data);
foreach (var s in studentsDe)
{
Console.WriteLine($@"姓名:{s.Name} 年龄:{s.Age} 性别:{studentDe.Gender}");
}
// 输出:
// 姓名:Job 年龄:28 性别:男
// 姓名:Jordan 年龄:30 性别:男
// 姓名:Steve 年龄:27 性别:男

正确的操作

上面我们是为了举例子才这么做,一般情况下我们都是这么去做的,除非有特殊需求,比如要将一个对象序列化之后赋值到某个对应数据库表某字段之上,然后存储到数据库中,当我们再用的时候就需要这样的操作了。

1
2
3
4
5
6
7
8
class ResponseData
{
public int Code { get; set; }

public string Message { get; set; }
// 将 Data 的类型从 string 改为 List<Student> 类型
public List<Student> Data { get; set; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ResponseData responseData = new ResponseData
{
Code = 1,
Message = "Hello 序列化与反序列化",
Data = new List<Student>
{
new Student { Name = "Job", Age = 28, Gender = "男" },
new Student { Name = "Jordan", Age = 30, Gender = "男" },
new Student { Name = "Steve", Age = 27, Gender = "男" }
}
};
string responseDataText = JsonConvert.SerializeObject(responseData);
Console.WriteLine(responseDataText);
// 输出:{"Code":1,"Message":"Hello 序列化与反序列化","Data":[{"Name":"Job","Age":28,"Gender":"男"},{"Name":"Jordan","Age":30,"Gender":"男"},{"Name":"Steve","Age":27,"Gender":"男"}]}
// 这样我们只做了一次序列化与反序列化,json文本也没有了很多的 \(反斜杠)。代码也相对于之前也更优雅了些。

注:我还是想说说为什么要举这个例子,因为种种原因,你想想不到的原因,你就是需要处理这样愚蠢的数据,前端相对于后端他可以使用极其方便的方式去处理这样的数据,但我们后端的处理起来代码就不会显得很优雅,因为明明一次就可以处理好的数据我们要反序列化两次。

Json 数据格式文本

JSON 是 JavaScript Object Notation 的简称,它是一种轻量的数据表示方法。它采用的是 key:value 的方式记录数据,相对于其他的数据表示方法这种方式更直观,在 Web 领域用的非常多。

  1. 最基本的数据单元 key:value
1
{ "name":"Job" }
  1. 多个 key:value 组成一条记录
1
{ "name": "Job", "age": 28, "gender": "男" }
  1. 由多个 key:value 组成的数组,用中括号表示,每个记录之间用逗号(,)分隔。
1
2
3
[{ "name": "Job", "age": 28, "gender": "男" },
{ "name": "Jordan", "age": 30, "gender": "男" },
{ "name": "Steve", "age": 27, "gender": "男" }]
  1. 为数组分配一个 key

students 是一条新记录一个 key ,它的值为一个数组。

1
2
3
4
5
6
{
"students":
[{ "name": "Job", "age": 28, "gender": "男" },
{ "name": "Jordan", "age": 30, "gender": "男" },
{ "name": "Steve", "age": 27, "gender": "男" }]
}
  1. 我们还可以将多个 students 组成一个更大的数组,记录与记录之间用逗号分隔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"Data":[
{
"students":
[{ "name": "Job", "age": 28, "gender": "男" },
{ "name": "Jordan", "age": 30, "gender": "男" },
{ "name": "Steve", "age": 27, "gender": "男" }]
},
{
"students":
[{ "name": "Job", "age": 28, "gender": "男" },
{ "name": "Jordan", "age": 30, "gender": "男" },
{ "name": "Steve", "age": 27, "gender": "男" }]
},
{
"students":
[{ "name": "Job", "age": 28, "gender": "男" },
{ "name": "Jordan", "age": 30, "gender": "男" },
{ "name": "Steve", "age": 27, "gender": "男" }]
}]
}

序列化的方式

  1. JSON
  2. Protobuf
  3. MessagePack
  4. MemoryPack

更多的请参考

bing搜索

https://www.jianshu.com/p/6219d8024d2c

https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.html

https://cloud.tencent.com/developer/article/1420284

.NET性能优化-是时候换个序列化协议了