C#数据加密-序列化

本文详细介绍了C#中的序列化概念,包括序列化的用途、不同方式如二进制、XML、JSON和数据契约的实现,以及如何在实际应用中保存和读取数据,强调了序列化在编程中的重要性及其在数据持久化、跨平台交互等方面的应用。
摘要由CSDN通过智能技术生成

一、问题引入

我们在进行编程的时候,对于数据常常有这样的需求:我们将数据写入文件,文件我们可以绕过程序找到,但是我们不想文件被人打开查看里面的内容,我们就可以使用C#中的序列化进行加密。

当然序列化的作用不仅于此:

1.1序列化

在C#中,序列化是将对象的状态信息转换为可以存储或传输的形式的过程。这个过程通常涉及将对象转换为一个字节序列(二进制序列化),或者转换成可读的格式,如XML或JSON(文本序列化)。序列化对于以下场景非常重要:

  1. 持久化数据: 通过序列化,你可以将对象持久化到文件、数据库、或者其他存储介质中。这样,即使程序终止,对象的状态也可以在之后被重新加载和使用。

  2. 通信: 在不同的应用程序之间或者不同的执行环境(如客户端和服务器)之间传输对象时,需要将对象序列化为一种标准格式,这样接收方才能够反序列化并重建对象。

  3. 深拷贝: 序列化还可以用来执行对象的深拷贝,即创建一个对象的完全独立副本,其中所有的子对象都是新的实例,而不是对原始对象的引用。

  4. 跨平台兼容性: 序列化允许跨语言或跨平台的数据交换,因为序列化后的格式通常是标准化的,不同的系统可以理解和处理。

  5. 安全或隔离: 对传输的数据进行序列化可以为数据添加安全层,例如,可以在数据传输之前对其进行加密,并在接收后解密。

  6. 缓存: 为了提高性能,应用程序可以将对象序列化后存储在缓存中,这样可以快速地加载和使用这些对象,而不是每次都重新创建它们。

序列化可以是显式的,也可以是隐式的。显式序列化通常是你直接调用序列化相关的API来执行的,如.NET框架提供的BinaryFormatter, DataContractSerializer, XmlSerializer, JavaScriptSerializer等。隐式序列化可能会发生在如远程过程调用(RPC)或者在某些框架和组件中,当需要自动传输或保存数据的时候。总之,序列化是现代编程中一个非常关键的特性,它使得对象数据的管理和传输成为可能。

1.2序列化的方式

在C#中,序列化可以通过多种方式进行,各种方式适用于不同的场景和需求。以下是一些常见的序列化方法:

二进制序列化: 利用 BinaryFormatter 类,可以将对象序列化为二进制格式。这种格式紧凑但不可读,适合性能敏感和带宽受限的场景。

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

// 创建对象
MyObject obj = new MyObject();

// 创建一个BinaryFormatter对象进行序列化
BinaryFormatter bf = new BinaryFormatter();

// 创建文件流
using (FileStream fs = File.Create("MyObject.dat"))
{
    // 序列化对象
    bf.Serialize(fs, obj);
}

2.XML序列化: 利用 XmlSerializer 类,可以将对象序列化为XML格式。XML是一种广泛使用的可读文本格式,适用于需要可读性或跨平台兼容性的场景。

using System.IO;
using System.Xml.Serialization;

// 创建对象
MyObject obj = new MyObject();

// 创建一个XmlSerializer对象进行序列化
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));

// 创建文件流
using (TextWriter writer = new StreamWriter("MyObject.xml"))
{
    // 序列化对象
    serializer.Serialize(writer, obj);
}

3.JSON序列化: 通过使用 System.Text.Json(.NET Core 3.0及以上版本)或 Newtonsoft.Json(常用于.NET Framework或早期版本.NET Core)可以将对象序列化为JSON格式,这种格式在Web开发中非常流行。

using System.Text.Json;

// 创建对象
MyObject obj = new MyObject();

// 序列化对象到JSON字符串
string jsonString = JsonSerializer.Serialize(obj);

// 将JSON字符串写入文件
File.WriteAllText("MyObject.json", jsonString);

4.数据契约序列化: 使用 DataContractSerializer, 面向服务或WCF(Windows Communication Foundation)应用程序设计而提供了更多的配置选项。

using System.IO;
using System.Runtime.Serialization;

// 创建对象
MyObject obj = new MyObject();

// 创建一个DataContractSerializer对象进行序列化
DataContractSerializer dcs = new DataContractSerializer(typeof(MyObject));

// 创建文件流
using (Stream stream = File.Create("MyObject.xml"))
{
    // 序列化对象
    dcs.WriteObject(stream, obj);
}

序列化之前需要确保对象是可序列化的,对于二进制序列化,通常需要在类前面加上 [Serializable] 属性。对于XML序列化,必须有一个无参构造函数,并且序列化的属性和字段需要是public的。对于数据契约序列化,需要在类和需要序列化的成员上标注 [DataContract] 和 [DataMember] 属性。对于JSON序列化,通常没有特别的要求,但对象的结构应该符合JSON能表示的数据结构。

序列化后的数据可以保存到文件中,也可以通过网络传输给其他应用程序或服务。对端应用程序可以使用相应的反序列化方法来重建原始对象。

二、举例(实际应用)

假如我们用Windows 窗口应用程序存储学生的信息,姓名,性别,年龄,生日
我们先创建一个学生的类,根据上面讲的内容,我们在进行序列化前要进行可序列化标记,因为我们打算用二进制序列,所以使用[Serializable]
在这里插入图片描述

2.1保存数据

//封装数据
Student objStudent = new Student()  //创建对象
            {
                Name = this.txtName.Text.Trim(),
                Age = Convert.ToInt16(this.txtAge.Text.Trim()),
                Gender = this.txtGender.Text.Trim(),
                Birthday = Convert.ToDateTime(this.txtBirthday.Text.Trim())
            };
            //【1】创建文件流
            FileStream fs = new FileStream("objStudent.stu", FileMode.Create);
            //[2]创建二进制格式化器
            BinaryFormatter formatter = new BinaryFormatter();
            //[3]调用序列化方法
            formatter.Serialize(fs, objStudent);
            //[4]关闭文件流
            fs.Close();

这里要注意以下几点:
1.对Student对象进行封装的原因:
通过创建一个 Student 对象并设置其属性,代码更加清晰和易于理解。这样的封装表明了你是在处理一个有具体结构和属性的数据实体,而不是零散的、无关联的数据。

另外 封装对象使得你可以在不同的方法中以对象为单位重用代码,而不必每次都重复设置个别的数据项。

其次,在一个对象内部封装相关的数据能确保数据的完整性。在这个例子中,Student 对象的属性(Name、Age、Gender、Birthday)被一起处理,这有助于确保所有相关信息在序列化和反序列化过程中保持一致。

最后 如果将来需要添加、删除或更改学生属性,封装将使得这些变更更加集中和容易管理。你只需要在 Student 类中做修改,而不是在代码的多个地方修改序列化和反序列化的逻辑。

2.创建文件流,这个可以参照C#文件读写里面的内容。

3.创建二进制格式化器
BinaryFormatter 类在.NET Framework中用于二进制序列化和反序列化对象。当使用 BinaryFormatter.Serialize 方法时,所传递的对象会被转换为二进制流。这里简要说明这个过程的原理:

1). 对象图遍历: 序列化过程从根对象开始,递归地遍历对象中的所有字段,包括简单类型的字段(如 int, double 等)、复杂类型字段(如其他类的实例)、数组、集合等。对于每个被访问的对象,都会检查它是否已被序列化过,以避免循环引用导致的无限循环。

2). 转换字段为二进制: 对象的每个字段值被转换成二进制数据。对于基础类型,这通常是直接的,因为存在明确的映射(例如,一个 int 类型会被转换为四个字节)。对于复杂类型,会先记录类型信息,然后将其字段递归序列化。

3). 保留类型信息: 为了在反序列化时能够重建对象,序列化过程中会保留对象的类型信息。这通常涉及记录对象的完整类型名称(包括其命名空间和程序集)。

4). 处理对象身份: 如果在对象图中多次引用同一个对象实例,BinaryFormatter 会处理这种情况,确保在反序列化时能够重建相同的引用关系,而不是创建多个副本。

5). 流写入: 序列化后的二进制数据会被写入到提供的 FileStream 中。这个数据流可能包含大量的字节,这些字节按照特定格式排列,以便于 BinaryFormatter 可以准确地重建对象图。

6). 支持自定义序列化: 如果对象实现了 ISerializable 接口,BinaryFormatter 会调用该接口的 GetObjectData 方法来获取对象要序列化的数据,这允许对象自定义其序列化过程。

使用 BinaryFormatter 的二进制序列化以紧凑和高效的格式存储对象,主要用于对象持久化和远程通信。但是,需要注意的是,由于安全和兼容性的问题,从.NET Core开始,BinaryFormatter 不再被推荐使用,而是推荐使用其他序列化方法,如 System.Text.Json.JsonSerializerSystem.Runtime.Serialization.Formatters.Binary

4.关闭文件流
这个在C#文件读写已经讲明。

我们运行成功后,输入:
在这里插入图片描述
发现在D:\IDE\my.obj这个路径中有了这样一个文件:
在这里插入图片描述
但是打不开,或者打开不是我们真实的内容。

2.2 读取数据

 private void btnDeserialize_Click(object sender, EventArgs e)
        {
            //【1】创建文件流
            FileStream fs = new FileStream("D:\\IDE\\my.obj", FileMode.Open);
            //[2]创建二进制格式化器
            BinaryFormatter formatter = new BinaryFormatter();
            //【3】调用反序列化方法
            Student objStudent = (Student)formatter.Deserialize(fs);
            //[4]关闭文件流
            fs.Close();


            this.txtName.Text = objStudent.Name;
            this.txtAge.Text = objStudent.Age.ToString();
            this.txtGender.Text = objStudent.Gender;
            this.txtBirthday.Text = objStudent.Birthday.ToShortDateString();
        }

这里面要注意的就是下面这一句里面的强制类型转换

Student objStudent = (Student)formatter.Deserialize(fs);

下面说明一下原因:

在.NET中,序列化和反序列化的过程涉及将对象状态转换为可存储的格式(如二进制),以及将这种格式转换回对象实例。BinaryFormatter.Deserialize 方法将二进制数据转换为最初序列化时的对象类型,但是返回值是 object 类型的,因为在.NET中所有类型都继承自 object

当你调用 formatter.Deserialize(fs) 方法时,它读取二进制数据流 fs 并根据其中的信息创建一个新的对象。因为 Deserialize 方法的返回类型是 object,即最基本的数据类型,你需要将其转换回原始的具体类型(在本例中是 Student)以便能够访问特定的属性和方法。

这种转换被称为强制类型转换(或类型转换),它告诉编译器你期望的返回对象的具体类型。在这个例子中,如果序列化的数据确实代表了一个 Student 对象,那么这个强制转换就是安全的。转换之后,你就可以将对象赋值给一个 Student 类型的变量,并访问其特有的属性和方法。

如果序列化的数据不是 Student 类型,这个强制转换将会失败并抛出一个 InvalidCastException,提示无法将对象从一种类型转换为另一种类型。

在C# 7.0及以后的版本中,我们可以使用模式匹配来进行更为安全的类型转换,例如:

if(formatter.Deserialize(fs) is Student objStudent)
{
    // 使用 objStudent
}
else
{
    // 处理错误情况
}

这种方式更加安全,因为它会在类型不匹配时执行另一段代码,而不是抛出异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值