对于我这样一个半路的业余爱好者,我想,我只需要方便和实现我需要的功能,但微软咋就做的不那么方便的呢?我想序列化就是能实现一个对象,一个控件的参数可以直接存成文件(即序列化),用反序列化即可反向构建这个对象和控件参数,属性值等。
比如,我就需要界面上的TreeView1(有很多子节点)序列化直接存为文件,反序列化即可一下子,将其内存copy到另外一个TreeView2上构建出相同的对象。这样,就是导入导出参数功能的了。放大,则是整个UI界面,可序列化的控件标识一下,序列化为文件,反序列化的时候就一下构建出各个可序列化的控件,不能构建的,则空白即可。
但,比较遗憾的是,至少没有这么直观的方法,不晓得微软咋想的,安全性原因?!不深入讨论这个问题了,说说别的。
2.Bin序列化和xml序列化:
1).以前,总想用个可以找个能读取Bin文件的工具,我就能看见里面的序列化对象名称和参数值的了,但很遗憾,费了很多心思也没有,结果查到的消息就是微软不想让你看见内容的。Bin序列化优点就是方便,非明文,安全性高,什么internal,private之类的字段都能序列化。
2).XML序列化则是可以看见明文,这样,若增加了字段,以前保存的参数文件,至少我能明文看见怎么参数值的,可手动再设置一遍。缺点是字段都必须是public属性才能xml序列化。
3.对于常常用到的TreeView不能直接序列化,比较愤恨,一大堆子节点,不小心就弄错的。而且,不想学仔细研究一行行去序列化各个字段,太繁琐。所以,百度一些方法,整理了一下,找了一个解决办法。
1.TreeViewDataAccess.cs(150703):
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; using System.Runtime.Serialization.Formatters.Binary; using System.Data; // //v1.1 2015-7-03 // namespace SerializerTool { public class TreeViewAdvanced { public TreeViewAdvanced() { } //删除也可以的 /// TreeViewData [Serializable()] public struct TreeViewSt { public TreeNodeData[] Nodes; /// 递归初始化TreeView数据 public TreeViewSt(TreeView tv) { Nodes = new TreeNodeData[tv.Nodes.Count]; if (tv.Nodes.Count == 0) return; for (int i = 0; i <= tv.Nodes.Count - 1; i++) { Nodes[i] = new TreeNodeData(tv.Nodes[i]); } } /// 通过TreeViewSt弹出TreeView public void PopulateTree(TreeView tv) { if (this.Nodes == null || this.Nodes.Length == 0) return; tv.BeginUpdate(); for (int i = 0; i <= this.Nodes.Length - 1; i++) { tv.Nodes.Add(this.Nodes[i].ToTreeNode()); } tv.EndUpdate(); } } /// TreeNodeData [Serializable()] public struct TreeNodeData { public string Text; public int ImageIndex; public int SelectedImageIndex; public bool Checked; public bool Expanded; public object Tag; public TreeNodeData[] Nodes; /// TreeNode构造函数 public TreeNodeData(TreeNode node) { this.Text = node.Text; this.ImageIndex = node.ImageIndex; this.SelectedImageIndex = node.SelectedImageIndex; this.Checked = node.Checked; this.Expanded = node.IsExpanded; this.Nodes = new TreeNodeData[node.Nodes.Count]; if ((!(node.Tag == null)) && node.Tag.GetType().IsSerializable) { this.Tag = node.Tag; } else { this.Tag = null; } if (node.Nodes.Count == 0) return; for (int i = 0; i <= node.Nodes.Count - 1; i++) { Nodes[i] = new TreeNodeData(node.Nodes[i]); } } /// TreeNodeData返回TreeNode public TreeNode ToTreeNode() { TreeNode ToTreeNode = new TreeNode(this.Text, this.ImageIndex, this.SelectedImageIndex); ToTreeNode.Checked = this.Checked; ToTreeNode.Tag = this.Tag; if (this.Expanded) ToTreeNode.Expand(); if (this.Nodes == null && this.Nodes.Length == 0) return null; if (ToTreeNode != null && this.Nodes.Length == 0) return ToTreeNode; for (int i = 0; i <= this.Nodes.Length - 1; i++) { ToTreeNode.Nodes.Add(this.Nodes[i].ToTreeNode()); } return ToTreeNode; } } /// 加载TreeView public static void LoadTreeViewData(TreeView tv, string path) { BinaryFormatter ser = new BinaryFormatter(); Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); TreeViewSt treeData = ((TreeViewSt)(ser.Deserialize(file))); treeData.PopulateTree(tv); file.Close(); } /// 保存TreeView到文件 public static void SaveTreeViewData(TreeView tv, string path) { BinaryFormatter ser = new BinaryFormatter(); Stream file = new FileStream(path, FileMode.Create); ser.Serialize(file, new TreeViewSt(tv)); file.Close(); } } //柳永法加的,序列化/反序列化DataTable class SerializeDataTable { public static DataTable LoadDataTable(string path) { BinaryFormatter bf = new BinaryFormatter(); Stream sm = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); DataTable dt = (DataTable)bf.Deserialize(sm); sm.Close(); return dt; } public static void SaveDataTable(DataTable dt, string path) { BinaryFormatter bf = new BinaryFormatter(); Stream sm = new FileStream(path, FileMode.Create); bf.Serialize(sm, dt); sm.Close(); } } }
2.BinarySerializer.cs:
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Windows.Forms; namespace SerializerTool { [Serializable] public class BinarySerializer { public static void Serialize<T>(T o,string filePath) { try { BinaryFormatter formatter = new BinaryFormatter(); Stream sm = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); formatter.Serialize(sm, o); sm.Flush(); sm.Close(); } catch(Exception ex) { MessageBox.Show(ex.Message); } } public static T DeSerialize<T>(string filePath) { try { BinaryFormatter formatter = new BinaryFormatter(); Stream sm = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); T o = (T)formatter.Deserialize(sm); sm.Flush(); sm.Close(); return o; } catch (Exception) { } return default(T); } } }
3.XmlSerializer.cs:
using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; using System.IO; using System.Windows.Forms; namespace SerializerTool { [Serializable] public class XMLSerializer { public static void Serialize<T>(T o,string filePath) { try { XmlSerializer formatter = new XmlSerializer(typeof(T)); StreamWriter sw = new StreamWriter(filePath, false); formatter.Serialize(sw,o); sw.Flush(); sw.Close(); } catch(Exception ex) { MessageBox.Show(ex.Message); } } public static T DeSerialize<T>(string filePath) { try { XmlSerializer formatter = new XmlSerializer(typeof(T)); StreamReader sr = new StreamReader(filePath); T o = (T)formatter.Deserialize(sr); sr.Close(); return o; } catch (Exception) { } return default(T); } } }
4.使用方法,放2个多节点的treeview,以xml序列化和反序列化为例(bin同理):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using SerializerTool; namespace SerializerTool { [Serializable] public struct TVSt { public TreeViewAdvanced.TreeViewSt inTV; //对应treeview1 public TreeViewAdvanced.TreeViewSt outTV; //对应treeview2</span></p><p><span style="color:#3333FF;"> }</span></p><p>.....................................</p><p> private void button11_Click(object sender, EventArgs e) //xml序列化方法 { TVSt myTv = new TVSt(); myTv.inTV = new TreeViewAdvanced.TreeViewSt(treeView1); myTv.outTV = new TreeViewAdvanced.TreeViewSt(treeView2); XMLSerializer.Serialize(myTv, "mytv.xml"); } private void button13_Click(object sender, EventArgs e) //xml反序列化方法,此例实现的是对调2个treeview内容的演示 { treeView1.Nodes.Clear(); treeView2.Nodes.Clear(); TVSt myTv; myTv = XMLSerializer.DeSerialize<TVSt>("mytv.xml"); //反序列化,得到包含多个treeview的结构体 myTv.inTV.PopulateTree(treeView2); //还原结构体内容,需要指定对应关系 myTv.outTV.PopulateTree(treeView1); //同上 } }
总之,若界面上的一些控件或者一些参数,需要放到一个结构体之内,然后对此定义的结构体进行序列化和反序列化,才可以实现我们所想要的导入导出参数功能。
为了防止忘记,note一下:
XmlSerializer 对象的Xml序列化和反序列化
[Serializable] ------后面的内容可序列化的类或者结构体(含多个字段)
[NonSerialized] ------ 用于Bin序列化中,不序列化的字段前加一个这个,序列化时就不输出其内容
[System.Xml.Serialization.XmlIgnoreAttribute] ---- 类似于NonSerialized, xml序列化时,不输出其指定字段的内容
网上,这些不是经常提到的。
2015.07.04 北京
新提示:若xml文件中有类似以下字符的,会被中断而xml反序列化不成功的哈!反正是不支持,这个花了我好多天才发现问题所在的地方的,好累眼睛!�代表byte中的0;
<prgName>www�����������������</prgName>
产生原因:string和byte[]之间转换造成,
之前使用的userPrgInfo.prgName = Encoding.GetEncoding("gbk").GetString(prg.ts.programName);
变为userPrgInfo.prgName = Encoding.GetEncoding("gbk").GetString(prg.ts.programName).TrimEnd('\0');即可解决。
网上有朋友有别的方法,如下:
如果反序列化时未给这种做特殊处理,反序列化将失败,如果xml中包含有未编码的低位字符,XmlDocument对象将报错;
替换掉未编码的低位序列字符,例如替换掉:
public static string Repalce(string str) { return System.Text.RegularExpressions.Regex.Replace(str, @"[\x00-\x08]|[\x0B-\x0C]|[\x0E-\x1F]", ""); }
替换后xml正常加载
8:28 2015-8-5
5.XmlSerializer.cs的扩展,允许多种类型:
using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; using System.IO; using System.Windows.Forms; using Shawn.WL.WanLongPannel.Dll; //using Shawn.WL.WanLongPannel.devs; namespace KTools.Serializer { [Serializable] public class XMLSerializer { public static Type[] extraTypes; //添加可能使用到的类型 public static void SetTpyes() { extraTypes = new Type[26]; extraTypes[0] = typeof(System.Collections.ArrayList); extraTypes[1] = typeof(AcItemSt_v1); extraTypes[2] = typeof(AcItemSt_v2); extraTypes[3] = typeof(AcItemSt_v3); extraTypes[4] = typeof(CA_descriptor); extraTypes[5] = typeof(CaRuleSt_item); extraTypes[6] = typeof(CasFlagSt); extraTypes[7] = typeof(CasSt); extraTypes[8] = typeof(ChannelProgramSt); extraTypes[9] = typeof(Chn_ca_st); extraTypes[10] = typeof(Commdes_st); extraTypes[11] = typeof(DataStream_st); extraTypes[12] = typeof(DataStream_st); extraTypes[13] = typeof(Dev_prgInfo_st); extraTypes[14] = typeof(EncoderPartStFstV2); extraTypes[15] = typeof(MuxDbSaveSt); extraTypes[16] = typeof(MuxPidInfo_st); extraTypes[17] = typeof(MuxPrgInfoGet_st); extraTypes[18] = typeof(Nit_section_st); extraTypes[19] = typeof(ScramblePidSt_v2); extraTypes[20] = typeof(ScramblePrgSt_v1); extraTypes[21] = typeof(ScramblePrgSt_v2); extraTypes[22] = typeof(ScramblePrgSt_v3); extraTypes[23] = typeof(Ts_loop2_st); extraTypes[24] = typeof(User_DataStream_st); extraTypes[25] = typeof(User_prgInfo_st); } public static void Serialize<T>(T o, string filePath) { SetTpyes(); try { XmlSerializer formatter = new XmlSerializer(typeof(T), extraTypes); // XmlSerializer formatter = new XmlSerializer(o.GetType()); StreamWriter sw = new StreamWriter(filePath, false); formatter.Serialize(sw, o); sw.Flush(); sw.Close(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } public static T DeSerialize<T>(string filePath) { SetTpyes(); try { XmlSerializer formatter = new XmlSerializer(typeof(T), extraTypes); StreamReader sr = new StreamReader(filePath); T o = (T)formatter.Deserialize(sr); sr.Close(); return o; } catch (Exception) { } return default(T); } } }
xml序列号比bin序列号失败概率大得多,所以,如何调试也是一个难题;
那就用到了之前的[System.Xml.Serialization.XmlIgnoreAttribute],它就是[XmlIgnore],用[XmlIgnore]需引用using System.Xml.Serialization;
xml序列号不成功,只看见一系列报错,而我们又不知道是哪个属性造成的,那就只有在类属性中一行一行注释测试,一般来说数组类,ArrayList等更容易出错;
例如:
[Serializable]
public struct Tuner6ASI2in2C_ConfigSt
{
public int devCode;
public MuxDbSaveSt muxDb;
//[System.Xml.Serialization.XmlIgnoreAttribute]
public object[] tuner; // TunerConfigSt_dvbS
//[System.Xml.Serialization.XmlIgnoreAttribute]
public UcCaDbSt[] caDb;
//[System.Xml.Serialization.XmlIgnoreAttribute]
public List<AcItemSt_v3> acList;
[System.Xml.Serialization.XmlIgnoreAttribute]
public List<ScramblePrgSt_v3>[] scr_Prog_arr;
// [XmlIgnore]
public int caCycle; // 加扰周期
// 2
public UcIpOut.UcIPOutDbSt ucIpOutDb;
public UcIpSrcDbSt ucIpOutSrc;
public UcIpDestDbSt4[] ucIpOutDest;
}
这么多数组,是在public List<ScramblePrgSt_v3>[] scr_Prog_arr;这里出错的,注释掉后就能生成xml文件的了。想办法弄成ArrayList才可以!否则就只能忽略该属性的了。
总结,不能xml序列化和反序列化的类型:
1.ArrayList数组:ArrayList[]
2.List数组:List[]
3.object数组:object[]
4.�代表byte中的0;
一般,数组才容易引起xml序列化不成功,但bin序列化没这些问题;而且,不是数组类型就不能xml序列化,例如:结构体数组,int[],bool[]等xml序列化没问题的,但object[]用xml序列化没成功。
15:59 2017/3/6