Dotnet反序列化入门各概念
目录
Dotnet在国内不算多,国内java安全偏多一点。 但是国外Dotnet很火,很多公司都是纯微软技术栈的,所以研究Dotnet还是有必要的。
本文默认读者有c#语法基础
Dotnet反序列化
本文很大程度参考了Y4er师傅的文章:https://github.com/Y4er/dotnet-deserialization/blob/main/dotnet-serialize-101.md
本小节很大程度参考了Y4er师傅的
先来一个最基本的Dotnet反序列化demo,了解一下Serializable、NonSerialized特性以及序列化、反序列化会用到的函数等。
using System;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleAppi1;
[Serializable] //一个需要被序列化的类需要加上Serializable特性
public class Person
{
public int age=10;
[NonSerialized]public String name="tom"; //NonSerialized特性用于标识不需要被序列化的属性
public String sex = "Male";
}
public class MyObject
{
static void Main(string[] args)
{
Person p1 = new Person();
FileStream fstream = new FileStream("E:/tools/dotnet/hellowold/Solution1/ser.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
BinaryFormatter binFormatterS = new BinaryFormatter();
binFormatterS.Serialize(fstream,p1);
BinaryFormatter binFormatterD = new BinaryFormatter();
fstream.Position = 0; //不加这个可能会报错
Person p2 = (Person)binFormatterD.Deserialize(fstream);
Console.WriteLine("age:"+p2.age);
Console.WriteLine("name:"+p2.name);
Console.WriteLine("sex:"+p2.sex);
}
}
查看序列化的数据,发现采用0001 0000开头
Formatter
Formatter用于设置序列化的格式,如我们上面用到了BinaryFormatter 进行二进制序列化,除此之外还有很多Formatter
如: SoapFormatter 用于序列化soap格式 LosFormatter 用于序列化 Web 窗体页的视图状态 XmlSerializer 用于生成XML 等
所有Formatter都最终继承自IFormatter接口,我们看看这个接口
可以发现定义了序列化、反序列化方法以及三个属性,通过这三个属性可以控制序列化、反序列化的过程。我们重点关注SurrogateSelector属性。
类 字段名 | 含义用途 |
---|---|
ISurrogateSelector SurrogateSelector | 序列化代理选择器 接管formatter的序列化或反序列化处理 |
SerializationBinder Binder | 用于控制在序列化和反序列化期间使用的实际类型 |
StreamingContext Context | 序列化流上下文 其中states字段包含了序列化的来源和目的地 |
BinaryFormatter序列化的生命周期和事件
ISerializable
我们先来看看实现ISerializable 接口的类 序列化、反序列化调用流程
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;
namespace ConsoleAppi1;
[Serializable]
public class Person:ISerializable
{
public Person()
{
}
//必须重载一个如下参数的构造方法,不重载的话会在反序列化时报错
protected Person(SerializationInfo info, StreamingContext context)
{
Console.WriteLine("Constructor Begin!");
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Console.WriteLine("GetObjectData");
}
[OnDeserializing]
private void TestOnDeserializing(StreamingContext sc)
{
Console.WriteLine("TestOnDeserializing");
}
[OnDeserialized]
private void TestOnDeserialized(StreamingContext sc)
{
Console.WriteLine("TestOnDeserialized");
}
[OnSerializing]
private void TestOnSerializing(StreamingContext sc)
{
Console.WriteLine("TestOnSerializing");
}
[OnSerialized]
private void TestOnSerialized(StreamingContext sc)
{
Console.WriteLine("TestOnSerialized");
}
}
public class MyObject
{
static void Main(string[] args)
{
Person p1 = new Person();
FileStream fstream = new FileStream("E:/tools/dotnet/hellowold/Solution1/ser.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
BinaryFormatter binFormatterS = new BinaryFormatter();
binFormatterS.Serialize(fstream,p1);
BinaryFormatter binFormatterD = new BinaryFormatter();
fstream.Position = 0;
binFormatterD.Deserialize(fstream);
}
}
运行结果如下
可以发现如果实现了ISerializable接口,那么序列化/反序列化流程则为:
序列化前:调用OnSerializing 特性的方法 序列化中: 调用GetObjectData 方法 序列化后:调用OnSerialized特性的方法 反序列化前:调用OnDeserializing特性的方法 反序列化中:调用有特定参数的构造方法 反序列化后:调用OnDeserialized特性的方法
代理选择器
再来看看实现代理选择器的类的序列化/反序列化流程
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;
namespace ConsoleAppi1;
[Serializable]
public class Person:ISerializable
{
public Person()
{
}
public Person(SerializationInfo info, StreamingContext context)
{
Console.WriteLine("Constructor Begin!");
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Console.WriteLine("GetObjectData");
}
[OnDeserializing]
private void TestOnDeserializing(StreamingContext sc)
{
Console.WriteLine("TestOnDeserializing");
}
[OnDeserialized]
private void TestOnDeserialized(StreamingContext sc)
{
Console.WriteLine("TestOnDeserialized");
}
[OnSerializing]
private void TestOnSerializing(StreamingContext sc)
{
Console.WriteLine("TestOnSerializing");
}
[OnSerialized]
private void TestOnSerialized(StreamingContext sc)
{
Console.WriteLine("TestOnSerialized");
}
}
//定义代理选择器,需要继承自ISerializationSurrogate,并实现GetObjectData,SetObjectData
public class MySerializationSurrogate : ISerializationSurrogate
{
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
Console.WriteLine("GetObjectData of ISerializationSurrogate");
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
Console.WriteLine("SetObjectData of ISerializationSurrogate");
return 1;
}
}
public class MyObject
{
static void Main(string[] args)
{
Person p1 = new Person();
FileStream fstream = new FileStream("E:/tools/dotnet/hellowold/Solution1/ser.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
BinaryFormatter binFormatter = new BinaryFormatter();
//设置代理选择器
SurrogateSelector ss = new SurrogateSelector();
ss.AddSurrogate(typeof(Person), binFormatter.Context, new MySerializationSurrogate());
binFormatter.SurrogateSelector = ss;
binFormatter.Serialize(fstream,p1);
fstream.Position = 0;
binFormatter.Deserialize(fstream);
}
}
Ref:
https://github.com/Y4er/dotnet-deserialization