二进制序列化与反序列化
一、核心知识点概念
1、序列化:将对象/集合数据,转换成二进制数据格式,写入本地文件持久化保存。
2、反序列化:读取本地二进制文件,将二进制数据重新还原为对象/集合的过程。
3、必备命名空间:
using`` ``System.Runtime.Serialization.Formatters.Binary;
4、类序列化特性 :自定义类必须标记 [Serializable] 特性,否则无法序列化,程序报错。
5、适用场景:批量保存集合数据、完整保存对象所有属性,相比文本读写,安全性更高、保密性更强。
二、完整源代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
namespace _3二进制序列化
{
public partial class Form1 : Form
{
//全局学生集合:存储所有学生对象
List<Student> list = new List<Student>();
public Form1()
{
InitializeComponent();
//1.打开文件流,读取本地二进制文件
FileStream fs = new FileStream(@"1.txt", FileMode.Open);
//2.实例化二进制格式化器
BinaryFormatter bf = new BinaryFormatter();
//3.反序列化:二进制数据转为学生集合
List<Student> temp = bf.Deserialize(fs) as List<Student> ;
//4.将本地旧数据追加到全局集合
list.AddRange(temp);
//5.释放资源
fs.Close();
fs.Dispose();
}
//序列化保存数据按钮
private void button1_Click(object sender, EventArgs e)
{
//1.创建学生对象
Student stu = new Student()
{
Name = "张三1",
Phone = "1232"
};
//2.添加到全局集合
list.Add(stu);
//3.创建文件流,覆盖模式写入文件
FileStream fs = new FileStream(@"1.txt", FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
//4.序列化:集合对象转为二进制写入文件
bf.Serialize(fs, list);
//5.释放资源
fs.Close();
fs.Dispose();
}
//反序列化读取数据按钮
private void button2_Click(object sender, EventArgs e)
{
//1.打开文件流读取文件
FileStream fs = new FileStream(@"1.txt", FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
//2.反序列化:二进制还原为集合
List<Student> ss = bf.Deserialize(fs) as List<Student>;
//3.遍历集合,拼接数据
string s = "";
foreach (var s1 in ss)
{
s += s1.Name + ":" + s1.Phone;
}
//4.数据展示到窗体标题
this.Text = s;
//5.释放资源
fs.Close();
fs.Dispose();
}
}
//序列化特性:允许该类对象进行二进制序列化
[Serializable]
public class Student
{
public string Name { get; set; }
public string Phone { get; set; }
}
}
三、代码分段详细拆解
1、可序列化实体类(核心重点)
[Serializable]
public class Student
{
public string Name { get; set; }
public string Phone { get; set; }
}
解析:
① [Serializable] 序列化特性:标记该类允许被二进制序列化;
② 没有该特性,序列化直接抛异常,无法保存对象;
③ 类中所有自动属性,都会被完整序列化保存。
2、全局集合定义
List<Student> list = new List<Student>();
定义全局学生集合,用来统一存储、管理所有学生对象,实现数据累加保存。
3、窗体构造方法:反序列化加载旧数据
//打开文件流
FileStream fs = new FileStream(@"1.txt", FileMode.Open);
//二进制格式化工具
BinaryFormatter bf = new BinaryFormatter();
//反序列化:二进制转集合
List<Student> temp = bf.Deserialize(fs) as List<Student> ;
//追加旧数据到全局集合
list.AddRange(temp);
执行时机:窗体打开自动执行
作用:程序启动自动读取本地保存的历史数据,避免每次重启数据丢失
语法说明:as 强制类型转换,将object类型转为学生集合类型
4、Button1 序列化写入(保存数据)
//创建对象并加入集合
Student stu = new Student(){ Name = "张三1", Phone = "1232" };
list.Add(stu);
//创建文件覆盖写入
FileStream fs = new FileStream(@"1.txt", FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
//序列化核心方法
bf.Serialize(fs, list);
核心方法:bf.Serialize(流对象, 需要序列化的对象/集合)
FileMode.Create:覆盖写入,将最新的集合数据重新保存到文件
5、Button2 反序列化读取(展示数据)
FileStream fs = new FileStream(@"1.txt",FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
//反序列化还原集合
List<Student> ss = bf.Deserialize(fs) as List<Student>;
//遍历拼接数据
string s = "";
foreach (var s1 in ss)
{
s+= s1.Name+":"+s1.Phone;
}
this.Text = s;
核心方法:bf.Deserialize(流对象) 读取二进制数据,还原为原始集合
遍历集合拼接所有学生信息,展示在窗体标题栏
四、序列化与反序列化流程
序列化流程:对象/集合 → BinaryFormatter → 二进制数据 → FileStream → 本地文件
反序列化流程:本地文件 → FileStream → 二进制数据 → BinaryFormatter → 还原对象/集合
五、核心类说明
1、BinaryFormatter:二进制格式化器,专门负责对象和二进制的相互转换
2、Serialize():序列化方法(对象 → 二进制)
3、Deserialize():反序列化方法(二进制 → 对象)
六、重难点总结
1、自定义类必须加 Serializable 特性,否则无法序列化;
2、二进制文件无法直接打开看懂,保密性优于普通文本读写;
3、支持整集合、整对象保存,无需拼接字符串,代码更简洁;
4、每次序列化使用 FileMode.Create,覆盖保存最新数据;
5、文件流使用完毕必须 Close、Dispose 释放资源。
七、易错点
1、忘记引入二进制命名空间,代码报错;
2、实体类未添加序列化特性,运行直接崩溃;
3、反序列化类型和序列化类型不一致,转换失败;
4、未释放文件流,导致文件被锁定占用。
XML序列化 & XmlDocument节点解析
一、XML基础概念
1、XML:可扩展标记语言,全部为成对标签(开始标签、结束标签)
2、结构组成:标签名、标签内容、属性、属性值
3、作用:弥补HTML结构不严谨的问题,用于结构化数据存储、数据传输
4、C#操作XML两大核心类:
XmlSerializer:快速实现 对象 ↔ XML 文件 序列化/反序列化
XmlDocument:DOM节点方式,手动遍历、解析XML内容
5、所需命名空间:
using System.Xml;
using System.Xml.Serialization;
二、实体类代码(数据模型)
1、People类(简单序列化测试类)
//用于快速XML序列化测试
public class People
{
public string Name { get; set; }
public int Age { get; set; }
}
2、Student类(对应XML学生节点)
//对应XML中Student标签的所有字段
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public string Phone { get; set; }
}
三、第一种用法:XmlSerializer 快速序列化(构造方法注释代码)
1、XML序列化(对象写入XML文件)
功能:C#对象 → 转为XML格式 → 保存到本地文件
//1.创建对象
People p = new People() { Age = 10, Name = "zs" };
//2.创建文件流,创建/覆盖xml文件
FileStream fs = new FileStream(@"1.xml", FileMode.Create);
//3.实例化XML序列化器,指定要序列化的类型
XmlSerializer xml = new XmlSerializer(typeof(People));
//4.序列化:将对象写入xml文件
xml.Serialize(fs, p);
//5.释放资源
fs.Close();
fs.Dispose();
2、XML反序列化(XML还原为对象)
功能:读取本地XML文件 → 还原为C#对象
//1.打开xml文件
FileStream fs = new FileStream(@"1.xml", FileMode.Open);
//2.创建序列化器
XmlSerializer xml = new XmlSerializer(typeof(People));
//3.反序列化,强制转为People对象
People p1 = (People)xml.Deserialize(fs);
//4.使用数据
this.Text = p1.Name;
//5.释放资源
fs.Close();
fs.Dispose();
四、第二种用法:XmlDocument 手动节点解析(核心重点)
适用场景:XML结构复杂、存在多余节点,需要手动筛选、精准读取数据
完整分段拆解(button1点击事件)
步骤1:创建集合与XML文档对象
//创建集合,用来存储所有读取到的学生对象
List<Student> list = new List<Student>();
//实例化XML操作核心对象
XmlDocument document = new XmlDocument();
步骤2:加载XML文件 + 获取根节点
//加载项目中的XML文件(../ 返回上级目录)
document.Load(@"../../XMLFile1.xml");
//获取XML最顶层根节点 <StudentList>
XmlNode root = document.DocumentElement;
步骤3:遍历根节点所有子节点,筛选学生节点
//遍历根节点下所有一级子节点
foreach (XmlNode node in root.ChildNodes)
{
//只处理<Student>节点,过滤多余的<a>杂节点
if (node.Name=="Student")
{
//每一个Student标签对应一个学生对象
Student stu = new Student();
步骤4:遍历学生内部子标签,给对象赋值
//遍历Student内部的子标签:Name、Age、Sex、Phone
foreach (XmlNode node1 in node.ChildNodes)
{
switch (node1.Name)
{
case "Name":
stu.Name = node1.InnerText; //InnerText 获取标签文本内容
break;
case "Age":
stu.Age = int.Parse(node1.InnerText); //字符串转int
break;
case "Sex":
stu.Sex = node1.InnerText;
break;
case "Phone":
stu.Phone = node1.InnerText;
break;
default:
break;
}
}
步骤5:对象入集合,完成单次遍历
//将赋值完成的学生对象加入集合
list.Add(stu);
}
}
步骤6:遍历集合,展示数据
//遍历所有学生,弹窗展示姓名
foreach (var item in list)
{
MessageBox.Show(item.Name);
}
五、配套XML文件内容(XMLFile1.xml)
<?xml version="1.0" encoding="utf-8" ?>
<StudentList>
<Student>
<Name>张三1</Name>
<Age>10</Age>
<Sex>男</Sex>
<Phone>121323232</Phone>
</Student>
<Student>
<Name>张三2</Name>
<Age>10</Age>
<Sex>男</Sex>
<Phone>121323232</Phone>
</Student>
<Student>
<Name>张三3</Name>
<Age>10</Age>
<Sex>男</Sex>
<Phone>121323232</Phone>
</Student>
<Student>
<Name>张三4</Name>
<Age>10</Age>
<Sex>男</Sex>
<Phone>121323232</Phone>
</Student>
<a>asasa</a>
</StudentList>
完整代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
namespace _6xml序列化
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
#region 【1.XmlSerializer 简单对象序列化(注释代码)】
////1.创建对象
//People p = new People() { Age = 10, Name = "zs" };
////2.创建文件流
//FileStream fs = new FileStream(@"1.xml", FileMode.Create);
////3.实例化XML序列化器,指定对应类类型
//XmlSerializer xml = new XmlSerializer(typeof(People));
////4.序列化:对象转XML写入文件
//xml.Serialize(fs, p);
//fs.Close();
//fs.Dispose();
#endregion
#region 【2.XmlSerializer 反序列化(注释代码)】
////1.打开XML文件
//FileStream fs = new FileStream(@"1.xml", FileMode.Open);
////2.创建序列化器
//XmlSerializer xml = new XmlSerializer(typeof(People));
////3.反序列化:XML还原为对象
//People p1 = (People)xml.Deserialize(fs);
//this.Text = p1.Name;
//fs.Close();
//fs.Dispose();
#endregion
}
//【核心案例:XmlDocument 手动遍历XML节点读取集合数据】
private void button1_Click(object sender, EventArgs e)
{
//1.创建集合,用于存储读取到的所有学生对象
List<Student> list = new List<Student>();
//2.实例化XML文档对象
XmlDocument document = new XmlDocument();
//3.加载指定路径的XML文件
document.Load(@"../../XMLFile1.xml");
//4.获取XML根节点 <StudentList>
XmlNode root = document.DocumentElement;
//5.遍历根节点下所有子节点
foreach (XmlNode node in root.ChildNodes)
{
//只处理 Student 节点,过滤其他杂节点
if (node.Name == "Student")
{
//每一个Student节点对应一个学生对象
Student stu = new Student();
//遍历Student内部的子标签(Name、Age、Sex、Phone)
foreach (XmlNode node1 in node.ChildNodes)
{
switch (node1.Name)
{
case "Name":
stu.Name = node1.InnerText;
break;
case "Age":
stu.Age = int.Parse(node1.InnerText);
break;
case "Sex":
stu.Sex = node1.InnerText;
break;
case "Phone":
stu.Phone = node1.InnerText;
break;
}
}
//将读取完成的学生对象加入集合
list.Add(stu);
}
}
//遍历集合弹窗展示所有学生姓名
foreach (var item in list)
{
MessageBox.Show(item.Name);
}
}
}
//测试实体类(简单对象序列化)
public class People
{
public string Name { get; set; }
public int Age { get; set; }
}
//学生实体类(对应XML节点字段)
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public string Phone { get; set; }
}
}
六、核心属性与方法总结
1、document.Load():加载本地XML文件
2、document.DocumentElement:获取XML根节点
3、node.ChildNodes:获取当前节点的所有子节点
4、node.Name:获取标签名称
5、node.InnerText:获取标签内部文本内容
6、Serialize():序列化(对象→XML)
7、Deserialize():反序列化(XML→对象)
七、两种XML操作对比
1、XmlSerializer:全自动转换,代码少,适合规整XML,无法过滤多余节点
2、XmlDocument:手动遍历节点,代码多,支持过滤无效节点,灵活度极高
八、易错点总结
1、项目内XML文件必须使用 ../../ 返回上级目录,否则路径报错
2、XML存在多余杂节点,必须判断节点名称过滤,否则会报错
3、Age为int类型,读取文本必须 int.Parse 转换类型
4、标签名称大小写严格区分,大小写不一致匹配不到数据
5、文件流使用完必须关闭释放,防止文件占用