C# 序列化与反序列化之Binary与Soap无法对泛型List<T>进行序列化的解决方案
新建Console控制台项目项目,然后添加Team和Person 这2个类,如下:
Team和Person 这2个类
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace SupremeConsole { [Serializable] public class Team { /// <summary> /// 队名 /// </summary> public string TName { get; set; } /// <summary> /// 选手 /// </summary> public List<Person> PlayerList = new List<Person>(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace SupremeConsole { [Serializable] public class Person { /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 年龄 /// </summary> public int Age { get; set; } } }
使用Binary或者Soap进行序列化,本例演示使用Soap对类型种的泛型字段进行序列化,代码如下:
using System; using System.Data; using System.Data.SQLite; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.IO.MemoryMappedFiles; using System.IO.Pipes; using System.Linq; using System.Net; using System.Security.AccessControl; using System.Security.Principal; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; using System.Reflection; using System.ServiceModel; using System.ServiceModel.Channels; using System.Runtime.Serialization; namespace SupremeConsole { class Program { static void Main(string[] args) { TestSeri(); Console.ReadLine(); } public static void TestSeri() { Team team = new Team { TName="123",PlayerList = { new Person { Name="1",Age=1},new Person { Name = "2", Age = 2 } } }; #region BinarySerialize 必须添可序列化属性,即要序列化的对象必须添加SerializableAttribute属性,[Serializable] //string s = SerializeManager.Instance.BinarySerialize<Team>(team);//序列化 //Console.ForegroundColor = ConsoleColor.Green; //Console.WriteLine("测试序列化成功。。。"); //Console.WriteLine($"测试序列化结果: {s}"); //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "序列化11111.bin");//序列化 //SerializeManager.Instance.BinarySerialize<Team>(team, path); //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "序列化11111.bin"); //Team test = SerializeManager.Instance.BinaryDeserialize<Team>(path);//反序列化 //if (test != null) //{ // Console.WriteLine($"测试序列化结果:{test.ToString()}"); //} #endregion #region SoapSerialize 必须添可序列化属性,即要序列化的对象必须添加SerializableAttribute属性,[Serializable] string s = SerializeManager.Instance.SoapSerialize<Team>(team);//序列化 Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("测试序列化成功。。。"); Console.WriteLine($"测试序列化结果: {s}"); //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Soap序列化.xml");//序列化 //SerializeManager.Instance.SoapSerialize<Team>(team, path); //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Soap序列化.xml"); //Team test = SerializeManager.Instance.SoapDeserialize<Team>(path);//反序列化 //if (test != null) //{ // Console.WriteLine($"测试序列化结果:{test.ToString()}"); //} #endregion } /// <summary> /// Binary泛型序列化 /// </summary> /// <typeparam name="T">泛型类型</typeparam> /// <param name="t">泛型对象</param> /// <returns>泛型对象序列化的字符串</returns> public string BinarySerialize<T>(T t) where T : class { string s = null; try { using MemoryStream ms = new MemoryStream(); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, t); s = Encoding.UTF8.GetString(ms.ToArray()); } catch (Exception ex) { Program.Log.Error($"Binary泛型序列化错误信息:{ex.ToString()}"); } return s; } /// <summary> /// Binary泛型序列化 /// </summary> /// <typeparam name="T">泛型类型</typeparam> /// <param name="t">泛型对象</param> /// <param name="path">保存的路径</param> public void BinarySerialize<T>(T t, string path) where T : class { try { //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt"); using FileStream fs = new FileStream(path, FileMode.OpenOrCreate); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, t); } catch (Exception ex) { Program.Log.Error($"Binary泛型序列化错误信息:{ex.ToString()}"); } } /// <summary> /// Binary泛型的反序列化 /// </summary> /// <typeparam name="T">泛型类型</typeparam> /// <param name="path">反序列化的序列化文件路径</param> /// <returns>泛型对象</returns> public T BinaryDeserialize<T>(string path) where T : class { T t = null; try { //string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt"); using MemoryStream fs = new MemoryStream(File.ReadAllBytes(path)); BinaryFormatter bf = new BinaryFormatter(); if (bf.Deserialize(fs) is T a) { t = a; } } catch (Exception ex) { Program.Log.Error($"Binary泛型的反序列化错误信息:{ex.ToString()}"); } return t; } }
}
运行程序报错,错误信息是无法对泛型进行序列化,
第一种解决方式:Soap序列化无法对泛型List<T>进行序列化,解决方法1 :使用[OnSerializing]特性解决泛型List<T>序列化,修改Team为下面代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace SupremeConsole { [Serializable] public class Team { #region 初始定义,使用Soap序列化PlayerList会报错,因为Soap序列化无法对泛型List<T>进行序列化,解决方法如下 ///// <summary> ///// 队名 ///// </summary> //public string TName { get; set; } ///// <summary> ///// 选手 ///// </summary> //public List<Person> PlayerList = new List<Person>(); #endregion #region Soap序列化无法对泛型List<T>进行序列化,解决方法1 :使用[OnSerializing]特性解决泛型List<T>序列化 /// <summary> /// 队名 /// </summary> public string TName { get; set; } Person[] _PlayerArr; /// <summary> /// 选手 /// </summary> [NonSerialized] public List<Person> PlayerList = new List<Person>(); [OnSerializing] public void SetPlayer(StreamingContext sc) { _PlayerArr = PlayerList.ToArray(); } [OnDeserialized] public void SetPlayered(StreamingContext sc) { _PlayerArr = null; } [OnDeserializing] public void GetPlayer(StreamingContext sc) { PlayerList = new List<Person>(_PlayerArr); } #endregion } }
在运行,可以序列化,但是反序列化的时候报错,集合为空错误,那么我们使用第二中解决方法,即实现ISerializable接口,
ISerializable接口中有GetObjectData(SerializationInfo info, StreamingContext context),序列化的时候会调用该方法,可以操作参数SerializationInfo ,SerializationInfo 是一个a name-value dictionary,
反序列化的时候,可以使用构造函数,构造函数的参数和GetObjectData的参数一样,即构造函数(SerializationInfo info, StreamingContext context)。
第二种解决方式:实现ISerializable接口对泛型List<T>进行序列化,修改Team为下面代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace SupremeConsole { [Serializable] public class Team : ISerializable { /// <summary> /// 队名 /// </summary> public string TName { get; set; } /// <summary> /// 选手 /// </summary> public List<Person> PlayerList = new List<Person>(); /// <summary> /// 序列化的时候会自动调用,使用virtual标记,示方便继承自类的使用 /// </summary> /// <param name="info"></param> /// <param name="context"></param> public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("TName", TName); info.AddValue("PlayerList", PlayerList); } public Team() { } /// <summary> /// 反序了列化的时候自动调用,使用protected标记,示方便继承自类的使用 /// </summary> protected Team(SerializationInfo info, StreamingContext context) { TName = info.GetString("TName"); PlayerList = (List<Person>)info.GetValue("PlayerList",typeof(List<Person>)); } } }
再运行程序,可以看到序列化和反序列化都可以了。
其实,Binary与Soap无法对泛型List<T>进行序列化的解决方案,无非就是使用[OnSerializing]特性和实现ISerializable接口,