||问题:
文本文档读取序列化文件时只能读取第一次序列化对象或对象集,而多次序列化存到同一个文本文件中不能完全读取。最近做一个简单的学生管理系统,涉及到多次将学生对象序列化后追加存储到同一个文档中。在查看所有学生的时候总是读取不完全,折腾了好长时间,想到了以下一个相对权宜之策。
1、对象序列化反序列化简述
对象序列化是指将对象转化成流的过程。
反序列化与之相反,是将流转换成对象。这两个过程组合起来,就使得数据能轻松的以对象或对象集为单位进行存储传输。
2、序列化反序列化步骤
用serializable标识类
调用BinaryFormatter、soapFormatter或XmlSerializer将其序列化
反序列调用Deserialize
3、简单对象序列化与反序列化
说明问题:第二次追加存储到第一次的文档后面,反序列化时就不能读取所有已经序列化的对象,而只能读取第一次序列化的对象(集)。下面是出现这个问题的程序。
参考程序:
//引用他人资源说明一下问题局限性。只能序列化一次,再次追加存储后,如果反序列化就不能取出所有对象 //----------二进制方式,可以使用BinaryFormatter 类来以二进制格式将对象或整个连接对象图形序列化和反序列化 using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Collections; using System.Collections.Generic; namespace Formatter { [Serializable] public class Account { public int UserID; public string Username; } class BinaryFormatterTest { public void BinaryFormat() { Account[] accounts = { new Account(){ UserID = 1, Username = "test1" }, new Account(){ UserID = 2, Username = "test2" }, new Account(){ UserID = 3, Username = "test3" } }; string savePath = @"e:BinarySerializerTest.bin"; BinaryFormatter formatter = new BinaryFormatter(); using (FileStream writeStream = new FileStream(savePath, FileMode.Append, FileAccess.Write)) //这边是Create没问题,但如果追加根本读不完啊 { formatter.Serialize(writeStream, accounts); //这里面只序列化一次,读者可以试着在序列化一次试试,或者在运行一次,追加存储 //formatter.Serialize(writeStream, accounts); //追加存储,取就会出问题 writeStream.Close(); using (FileStream readStream = new FileStream(savePath, FileMode.Open, FileAccess.Read)) { Account[] deSerializedValue = formatter.Deserialize(readStream) as Account[]; if (deSerializedValue != null && deSerializedValue.Length > 0) { for (int i = 0; i < deSerializedValue.Length; i++) { Console.WriteLine("{0} UserID = {1}, Username = {2}", i, deSerializedValue[i].UserID, deSerializedValue[i].Username); } } } } Console.ReadKey(); } } }
4、解决办法:
设第二次序列化的集合为List<Student>stuList,在第二次序列化之前,把第一次序列化的对象集反序列化取出,存储到List<类>的泛型化集合stuListRead中,然后用foreach语句将stuListRead中的所有元素添加到stuList中,然后一起序列化,覆盖原文件存储。
这里面会遇到一个问题,就是原文件为空的情况,反序列化取出存储到stuList集合时,就会出现“流为空”异常,读者可以在反序列化之前进行文件信息判断,fi.Length?=0;如果不等于0在进行反序列化即可。读者可以参考以下程序。
5、Student类
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace StudentManagement { /* 添加一个学生类Student,该类封装学生的一些基本信息(学号、姓名、年龄等字段), * 重载一个带参数的构造函数,为学生的基本信息字段初始化。*/ [Serializable] class Student { private int stuNum; //学号 public int StuNum { get { return stuNum; } set { stuNum = value; } } private string stuName; //姓名 public string StuName { get { return stuName; } set { stuName = value; } } private string stuSex; //性别 public string StuSex { get { return stuSex; } set { stuSex = value; } } private string stuMajor; //专业 public string StuMajor { get { return stuMajor; } set { stuMajor = value; } } private string stuClass; //班级 public string StuClass { get { return stuClass; } set { stuClass = value; } } //构造函数初始化 public Student(int stuNum, string stuName, string stuSex, string stuMajor, string stuClass) { this.stuNum = stuNum; this.stuName = stuName; this.stuSex = stuSex; this.stuMajor = stuMajor; this.stuClass = stuClass; } } }
6、FileAccessOperate文件操作类
导入命名空间
using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary;
定义一个文件目录项:
static string txtDictionary=@"e: 1111104";
反序列化读方法:
//读方法 public List<Student> StuRead() { string path=txtDictionary+"stu.dat"; //学生信息存储路径 FileStream stream = new FileStream(path,FileMode.Open,FileAccess.Read); BinaryFormatter bf = new BinaryFormatter(); //创建序列化对象 List<Student> stuList; stuList = bf.Deserialize(stream) as List<Student>; stream.Close(); return stuList; }
序列化写方法:
//写方法 public void StuWrite(List<Student> stuList) { string path = txtDictionary + "stu.dat"; //学生信息存储路径 FileInfo fi = new FileInfo(path); //判断文件是否存在 if (fi.Exists == false) { FileStream fs = new FileStream(path,FileMode.Create); fs.Close(); fi = new FileInfo(path); } //判断文件是否为空,即是否是第一次序列化存储 if (fi.Length > 0) { //如果不为空,即已经进行过序列化存储,需将之前存的信息,反序列化读取,添加到要存储的对象集,覆盖存储 List<Student> StuListRead = StuRead(); foreach (Student stu in StuListRead) { stuList.Add(stu); } } Stream stream = new FileStream(path, FileMode.Create, FileAccess.Write); BinaryFormatter bf = new BinaryFormatter(); //创建序列化对象 //序列化 bf.Serialize(stream, stuList); stream.Close(); }
7、总结
这样就能解决对象集每次只能读取第一次序列化的问题,这样能把所有存储的学生对象全部读出。关键在于每次序列化后都产生独立的对象集存储在文本文档中,够每次序列化只能读取第一个对象集。通过在写之前进行读取,和要写的对象集合并成一个对象集重新覆盖写入,实际上文本文档中只存储了一个对象集,这样就能反序列化读取所有对象。