WPF自帶的一些屬性比如System.Windows.Media.Brush、System.Windows.Media.FontFamily、System.Windows.FontWeight等不具有直接序列化能力,本文針對這一點設計名為PropertySerializateDictionary的類,實現了不同類不同對象公有屬性的序列化和反序列化。本類繼承於Dictionary<string, TypeAndValue>, IXmlSerializable,調用了自實現類TypeAndValue。
代碼:
/// <summary> /// 用於記錄對象和類型的類 /// 用在PropertySerializateDictionary中 /// 單元測試:PropertySerializateDictionaryTest /// </summary> public class TypeAndValue { public TypeAndValue(Type type, object value) { Type = type; Value = value; } public TypeAndValue() { } public Type Type { get; set; } public object Value { get; set; } public override bool Equals(object obj) { TypeAndValue tav = obj as TypeAndValue; if (tav == null) { return false; } var EqualsMehtod = Type.GetMethod("Equals"); if (tav.Type.FullName == this.Type.FullName && ((bool)EqualsMehtod.Invoke(tav.Value, new object[]{ this.Value})) ) { return true; }; return false; }
/// <summary> /// 不同類不同對象公有屬性的序列化和反序列化 /// 可序列化、反序列化WPF體系Color、Brush、FontFamily、FontWeight /// 無法識別集合類型與Struct?類型 /// </summary> /// <typeparam name="T"></typeparam> public class PropertySerializateDictionary : Dictionary<string, TypeAndValue>, IXmlSerializable { public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { if (reader.IsEmptyElement) { return; } reader.Read(); string TypeString = reader.GetAttribute("Type"); while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { string key = reader.Name; Type tpe = Type.GetType(TypeString); PropertyInfo[] props = tpe.GetProperties(); this[key] = new TypeAndValue(tpe, tpe.GetConstructor(new Type[] { }).Invoke(null)); if (!reader.IsEmptyElement) { //填充屬性值 reader.ReadStartElement(); if (reader.NodeType != System.Xml.XmlNodeType.EndElement) { foreach (PropertyInfo pi in props) { if (pi.PropertyType.FullName == "System.Windows.Media.Brush") { System.Windows.Media.BrushConverter brushConverter = new System.Windows.Media.BrushConverter(); pi.SetValue(this[key].Value, (System.Windows.Media.Brush)brushConverter.ConvertFromString(reader.ReadElementString(pi.Name)), null); //根據屬性的類型對讀取出來的字符串進行轉換 } else if (pi.PropertyType.FullName == "System.Windows.Media.FontFamily") { System.Windows.Media.FontFamilyConverter fontFamilyConverter = new System.Windows.Media.FontFamilyConverter(); pi.SetValue(this[key].Value, (System.Windows.Media.FontFamily)fontFamilyConverter.ConvertFromString(reader.ReadElementString(pi.Name)), null); } else if (pi.PropertyType.FullName == "System.Windows.FontWeight") { System.Windows.FontWeightConverter fontFamilyConverter = new System.Windows.FontWeightConverter(); pi.SetValue(this[key].Value, (System.Windows.FontWeight)fontFamilyConverter.ConvertFromString(reader.ReadElementString(pi.Name)), null); } else { pi.SetValue(this[key].Value, Convert.ChangeType(reader.ReadElementString(pi.Name), pi.PropertyType), null); } } } } reader.Read(); } } public void WriteXml(System.Xml.XmlWriter writer) { if (this.Keys.Count < 1) { return; } foreach (string name in Keys) { TypeAndValue tandv = new TypeAndValue(); writer.WriteStartElement(name); if (this.TryGetValue(name, out tandv)) { writer.WriteAttributeString("Type", tandv.Type.FullName); //存儲該類型的公有屬性 PropertyInfo[] props = tandv.Type.GetProperties(); //將對象的每個屬性名和屬性值寫入xml文件中 foreach (PropertyInfo prop in props) { //<屬性名>屬性值</屬性名> writer.WriteElementString(prop.Name, prop.GetValue(tandv.Value, null).ToString()); } } writer.WriteEndElement(); } } public override bool Equals(object obj) { PropertySerializateDictionary dic = obj as PropertySerializateDictionary; if (dic == null) { return false; } if (dic.Count != this.Count) { return false; } foreach (string key in dic.Keys) { if (!this[key].Equals(dic[key])) { return false; } } return true; } }
單元測試代碼如下:
[Serializable] public class SerialMock { // Private fields. string strFirstName = "<first name>"; DateTime dtBirthDate = new DateTime(1800, 1, 1); private FontWeight m_fontWeight = FontWeights.Heavy; [XmlElement(ElementName="XAxisProperty_Caption_FontWeight")] public FontWeight FontWeight { get { return m_fontWeight; } set { m_fontWeight = value; } } [XmlElement(ElementName = "XAxisProperty_Caption_FonFamily")] private FontFamily m_FontFamily = new FontFamily("TechnicBold"); public FontFamily FontFamily { get { return m_FontFamily; } set { m_FontFamily = value; } } public SerialMock() { } // Public properties. public string FirstName { set { strFirstName = value; } get { return strFirstName; } } [XmlElement(DataType = "date")] public DateTime BirthDate { set { dtBirthDate = value; } get { return dtBirthDate; } } public override bool Equals(object obj) { SerialMock p = obj as SerialMock; if (p == null) { return false; } return p.FirstName.Equals(this.FirstName) && p.BirthDate.Equals(this.BirthDate) && p.FontWeight.Equals(this.FontWeight); } public override int GetHashCode() { return this.FirstName.GetHashCode() ^ this.BirthDate.GetHashCode() ^ this.FontWeight.GetHashCode(); } public override string ToString() { return strFirstName.ToArray() + dtBirthDate.ToShortDateString() + FontWeight.ToString() + FontFamily; } }
/// <summary> ///ReadXml 的測試 ///</summary> [TestMethod()] public void ReadXmlTest() { Property_Setting.Add("People1", new TypeAndValue(typeof(SerialMock), new SerialMock() { FirstName = "a", FontWeight = FontWeights.Light, BirthDate=new DateTime(1984, 05, 17)})); Property_Setting.Add("People2", new TypeAndValue(typeof(SerialMock), new SerialMock() { FirstName = "b", FontWeight = FontWeights.Black, FontFamily = new System.Windows.Media.FontFamily("宋體") })); MemoryStream stream = new MemoryStream(); XmlSerializer serializer = new XmlSerializer(typeof(PropertySerializateDictionary)); serializer.Serialize(stream, Property_Setting); stream.Flush(); stream.Position = 0; //stream = new FileStream(System.AppDomain.CurrentDomain.BaseDirectory + "tst.axml", FileMode.Open); PropertySerializateDictionary dict = (PropertySerializateDictionary)serializer.Deserialize(stream); Assert.AreEqual<PropertySerializateDictionary>(Property_Setting, dict); stream.Close(); }