C# 二进制序列化与反序列化 +XML序列化 & XmlDocument节点解析

二进制序列化与反序列化

一、核心知识点概念

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、文件流使用完必须关闭释放,防止文件占用