• .net序列化与反序列化——提供多次存储对象集后读取不完全解决方案


    ||问题:

           文本文档读取序列化文件时只能读取第一次序列化对象或对象集,而多次序列化存到同一个文本文件中不能完全读取。最近做一个简单的学生管理系统,涉及到多次将学生对象序列化后追加存储到同一个文档中。在查看所有学生的时候总是读取不完全,折腾了好长时间,想到了以下一个相对权宜之策。

    1、对象序列化反序列化简述

          对象序列化是指将对象转化成流的过程。

          反序列化与之相反,是将流转换成对象。这两个过程组合起来,就使得数据能轻松的以对象或对象集为单位进行存储传输。

    2、序列化反序列化步骤

          用serializable标识类

          调用BinaryFormattersoapFormatterXmlSerializer将其序列化

          反序列调用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、总结

            这样就能解决对象集每次只能读取第一次序列化的问题,这样能把所有存储的学生对象全部读出。关键在于每次序列化后都产生独立的对象集存储在文本文档中,够每次序列化只能读取第一个对象集。通过在写之前进行读取,和要写的对象集合并成一个对象集重新覆盖写入,实际上文本文档中只存储了一个对象集,这样就能反序列化读取所有对象。

  • 相关阅读:
    运算符(C# 参考)
    virtualbox修改主机名
    偏前端HTML5 sessionStorage会话存储
    偏前端纯css,手写轮播(焦点切换 和 自动轮播 只可选择一种,两者不可共存)
    偏前端 div+mui+vue.js 制作问卷调查单页 ——题目答案由后台随机给出10道
    偏前端 jqueryiframe内触发父窗口自定义事件
    TWAIN扫描打印图像编辑处理控件ImagXpress功能大全
    实时温度计湿度计高效仪器控件Iocomp使用教程
    Essential Diagram for Windows Forms绘图控件图解及下载地址
    Code39 Fontware条形码控件
  • 原文地址:https://www.cnblogs.com/xiangyangzhu/p/4239803.html
Copyright © 2020-2023  润新知