xml序列化和反序列化方法
阿新 • • 發佈:2018-12-24
1、作用:將實體類的屬性(字串、日期、數值、布而、類)生成為xml文件中的結點,將xml文件的結點值或者屬性值填充到實體類的屬性值中
2、思路:特性、反射、泛型;特性記錄xml結點與實體屬性的對應關係,反射獲取和填充例項中的屬性值。 自定義NodeAttribute,兩個屬性Name和Index,Name用於對應實體類中屬性在xml文件中的結點名稱,Index反應不同結點在xml文件中的出現順序。
3、兩個類:
XmlNodeSerializer,實體<->XmlNode;因為有時候要操作的xml文件結構比較複雜,NodeAttribute不能實現所有需求,所以把簡單基礎的結點在裡做了,複雜的結點自己實現。
XmlSerializer:從XmlNodeSerializer繼承來的,實體<->XmlDocument
4、可設定的部分:
1)TimeFormat:可以設定例項中日期型別的屬性值的輸出格式;
2)NodeNameFormatter:可以對結點名稱格式化,屬性與結點的名稱對應關係a)如果設定了NodeAttribute,從NodeAttribute中取;b)如果沒有NodeAttribute預設屬性名;c)如果設定了NodeNameFormatter,再按NodeNameFormatter的規則格式化
3)CreateNodeWhenNull:屬性值為null時是否生成結點
4)ContainPropertyWithOutNodeAttr:屬性值沒有被NodeAttribute標記時,是否生成、解析結點
測試
public void Test1() { XmlSerializer xs = new XmlSerializer(); xs.TimeFormat = "yyyy-MM-dd"; xs.NodeNameFormatter = Format; xs.CreateNodeWhenNull = true; xs.ContainPropertyWithOutNodeAttr = true; Student stu= new Student(); stu.Name = "小小"; stu.Age = 10; stu.BirthDay = new DateTime(2008, 6, 7); Dog dog = new Dog(); dog.Color = "黑色"; dog.Age = 2; dog.Weight = 12.34; //dog.Name //null 測試CreateNodeWhenNull stu.Dog = dog; XmlDocument doc= xs.Serialize(stu); doc.Save("1.xml"); Student stu2 = xs.DeSerialize<Student>(File.ReadAllText("1.xml")); } private string Format(string attr) { if (string.IsNullOrEmpty(attr)) return null; StringBuilder sb = new StringBuilder(); char[] name = attr.ToCharArray(); for (int i = 0; i < name.Length; i++) { if (i.Equals(0)) { sb.Append(name[i].ToString().ToUpper()); } else { if (name[i] >= 'A' && name[i] <= 'Z') { sb.Append("_").Append(name[i].ToString()); } else { sb.Append(name[i].ToString().ToUpper()); } } } return sb.ToString(); } public class Student { [Node(Index = 2, Name = "MingCheng")] public string Name { get; set; } [Node(Index = 3)] public int Age { get; set; } [Node(Index = 1)] public DateTime? BirthDay { get; set; } [Node(Name = "GouGou")] public Dog Dog { get; set; } } public class Dog { [Node] public string Name { get; set; } [Node] public int? Age { get; set; } [Node] public double? Weight { get; set; } public string Color { get; set; } }
原始碼
/// <summary> /// xml結點序列化,反序列化 /// TimeFormat:設定日期屬性的格式 /// NodeNameFormatter:可以對結點名稱格式化 /// 結點名:如果設定了NodeAttr的Name,按設定取,否則預設取屬性名 /// CreateNodeWhenNull:設定屬性值為null時是否生成結點 /// ContainPropertyWithOutNodeAttr:屬性值沒有被NodeAttr標記時,是否生成、解析結點 /// </summary> public class XmlNodeSerializer { protected string timeFormat; protected Func<string, string> nodeFormatter; protected bool createNodeWhenNull = true; protected bool containPropertyWithOutNodeAttr = false; /// <summary> /// 日期格式 /// </summary> public string TimeFormat { set { timeFormat = value; } } /// <summary> /// 結點名稱格式化器 /// </summary> public Func<string, string> NodeNameFormatter { set { nodeFormatter = value; } } /// <summary> /// 空屬性值是否生成結點 /// </summary> public bool CreateNodeWhenNull { set { createNodeWhenNull = value; } } /// <summary> /// 屬性沒有被NodeAttr標記時,是否生成結點或解析,預設false /// </summary> public bool ContainPropertyWithOutNodeAttr { set { containPropertyWithOutNodeAttr = value; } } //如果node需要新增到doc中,則需要將doc作引數傳入 public XmlNode Serialize<T>(T data, XmlDocument doc) { Type type = typeof(T); string nodeName = string.Empty; //結點名 NodeAttribute na = type.GetCustomAttribute<NodeAttribute>(); if (na == null || na.Name.IsNullOrEmpty()) { nodeName = type.Name;//如果沒有設定,使用類名 } else { nodeName = na.Name; //取設定的結點名 } if (nodeFormatter != null) nodeName = nodeFormatter(nodeName); return Serialize(data, typeof(T), nodeName, doc); } public XmlNode Serialize<T>(T data) { return Serialize(data, new XmlDocument()); } private XmlNode Serialize(object data, Type type, string parentNodeName, XmlDocument doc) { if (data == null || parentNodeName.IsNullOrEmpty() || type == null || doc == null) return null; XmlNode root = doc.CreateElement(parentNodeName); Entity ent = new Entity(); //對自定義類、基礎資料型別的屬性值生成節點 var props = type.GetProperties().Where(p => ent.IsUserDefinedClass(p) || ent.IsNormalBaseType(p)); //有註解的屬性 var selfNodeProps = props.Where(p => { var cus = p.GetCustomAttributes(typeof(NodeAttribute), true); return cus != null && cus.Count() > 0; }).OrderBy(p => { NodeAttribute na = p.GetCustomAttributes(typeof(NodeAttribute), true)[0] as NodeAttribute; int i = na.Index; Console.WriteLine($"{na.Name},{na.Index}"); return i; }).ToArray(); List<PropertyInfo> pis = new List<PropertyInfo>(); //按註解裡的先後順序新增結點 if (selfNodeProps != null && selfNodeProps.Count() > 0) { pis.AddRange(selfNodeProps); } if (containPropertyWithOutNodeAttr) { //沒有註解的屬性 var defaultNodeProps = props.Where(p => { var cus = p.GetCustomAttributes(typeof(NodeAttribute), true); return cus == null || cus.Count() <= 0; }); //再新增沒有註解的結點 if (defaultNodeProps != null && defaultNodeProps.Count() > 0) { pis.AddRange(defaultNodeProps); } } foreach (var prop in pis) { string nodeName = prop.Name;//結點名,預設沒有註解,用屬性名 var attrs = prop.GetCustomAttributes(typeof(NodeAttribute), true); if (attrs != null && attrs.Count() > 0) { NodeAttribute attr = attrs[0] as NodeAttribute; if (attr.Name.IsNotNullOrEmpty())//註解裡有結點名 { nodeName = attr.Name; } } if (nodeFormatter != null)//如果設定了更改規則,按規則修改 { nodeName = nodeFormatter(nodeName); } if (ent.IsNormalBaseType(prop))//基礎資料型別,直接生成結點 { string nodeValue = null; object v = prop.GetValue(data); if (prop.PropertyType.ToString().Contains("DateTime")) { if (v != null) { if (timeFormat.IsNullOrEmpty()) timeFormat = "yyyyMMdd"; nodeValue = Convert.ToDateTime(v).ToString(timeFormat); } } else if (v != null) { nodeValue = Convert.ToString(v); } if (nodeValue == null && !createNodeWhenNull) { continue;//屬性值為null,且設定了null值不需要加入結點 } XmlNode node = doc.CreateElement(nodeName);//建立結點 node.AppendChild(doc.CreateCDataSection(nodeValue)); root.AppendChild(node);//拼接到根結點中 } else if (ent.IsUserDefinedClass(prop))//自定義類,生成複雜結點 { object v = prop.GetValue(data); if (v != null) { XmlNode node = Serialize(v, prop.PropertyType, nodeName, doc); if (node != null) { root.AppendChild(node); } } } } return root; } //對基礎資料型別屬性賦值 private void FillBaseType<T>(XmlNode node, T obj, IEnumerable<PropertyInfo> baseProps) { DataType myType = new DataType(); foreach (var prop in baseProps) { string name = GetNodeNameFromProp(prop); XmlNode xn = node.SelectSingleNode(name); string xnv = string.Empty; if (xn == null) { //如果沒有對應結點則從屬性中找 var attr = node.Attributes[name]; if (attr != null && attr.Value.IsNotNullOrEmpty()) { xnv = attr.Value; } else { continue; } } else if (xn.InnerText.IsNullOrEmpty()) { continue; } else { xnv = xn.InnerText; } //取屬性的資料型別 DbType dbt = myType.PropertyTypeToDbType(prop.PropertyType.ToString()); switch (dbt) { case DbType.AnsiString: prop.SetValue(obj, xnv, null); break; case DbType.DateTime: DateTime dt; if (timeFormat.IsNullOrEmpty())//如果沒有設定日期格式,按值長度自動設定 { if (xnv.Length.Equals(8)) timeFormat = "yyyyMMdd"; if (xnv.Length.Equals(14)) timeFormat = "yyyyMMddHHmmss"; } if (DateTime.TryParseExact(xnv, timeFormat, null, DateTimeStyles.None, out dt)) { prop.SetValue(obj, dt, null); } break; case DbType.Decimal: Decimal dec; if (Decimal.TryParse(xnv, out dec)) { prop.SetValue(obj, dec, null); } break; case DbType.Double: double db; if (Double.TryParse(xnv, out db)) { prop.SetValue(obj, db, null); } break; //Int32、Int16 3種類型處理方式一樣 case DbType.Int32: case DbType.Int16: int i; if (int.TryParse(xnv, out i)) { prop.SetValue(obj, i, null); } break; case DbType.UInt64: long li; if (long.TryParse(xnv, out li)) { prop.SetValue(obj, li, null); } break; case DbType.Boolean: bool b; if (Boolean.TryParse(xnv, out b)) { prop.SetValue(obj, b, null); } break; } } } //從prop裡取出對應的結點名,1、如果有NodeAttribute特性,則取特性裡的名稱,2、否則預設取屬性名,3、如果設定了變化規則,按規則變化 private string GetNodeNameFromProp(PropertyInfo prop) { string name = prop.Name;//預設為屬性名 var cus = prop.GetCustomAttributes(typeof(NodeAttribute), true); if (cus != null && cus.Length > 0) { string seflName = (cus[0] as NodeAttribute).Name; if (seflName.IsNotNullOrEmpty()) name = seflName;//註釋裡的結點名 } if (nodeFormatter != null) { name = nodeFormatter(name); } return name; } /*從node的子結點或屬性中解析資料到實體中*/ public T DeSerialize<T>(XmlNode node) where T : class { object obj = DeSerialize(typeof(T), node); return obj as T; } private object DeSerialize(Type type, XmlNode node) { if (node == null || type == null) return null; object obj = Activator.CreateInstance(type); var props = type.GetProperties(); if (props == null || props.Count() <= 0) return null; //如果不需要處理沒有被NodeAttr標記的屬性,過濾出有NodeAttr的屬性 if (!containPropertyWithOutNodeAttr) { props = props.Where(p => { var nas = p.GetCustomAttributes(typeof(NodeAttribute), true); return nas != null && nas.Count() > 0; }).ToArray(); } Entity ext = new Entity(); //先處理基礎資料型別 var baseProps = props.Where(p => ext.IsNormalBaseType(p)); if (baseProps != null && baseProps.Count() > 0) { FillBaseType(node, obj, baseProps); } //處理自定義類型別 var classProps = props.Where(p => ext.IsUserDefinedClass(p)); if (classProps != null && classProps.Count() > 0) { foreach (var prop in classProps) { string name = GetNodeNameFromProp(prop); XmlNode xn = node.SelectSingleNode(name); if (xn == null) continue; object v = DeSerialize(prop.PropertyType, xn); if (v != null) prop.SetValue(obj, v); } } return obj; } /* 從物件屬性中取出結點名稱和結點值 step1、如果有NodeAttribute,按順序取結點名 step2、如果沒有NodeAttribute,取屬性名作結點名 step3、如果設定了nodeFormatter,根據nodeFormatter更改結點名 */ private List<KeyValueEntity> GetAttrValue(object data) { List<KeyValueEntity> list = null; if (data != null) { Type type = data.GetType(); var props = new Entity().GetNormalBaseType(type.GetProperties()); list = new List<KeyValueEntity>(); //有註解的屬性 var selfNodeProps = props.Where(p => { var cus = p.GetCustomAttributes(typeof(NodeAttribute), true); return cus != null && cus.Count() > 0; }).OrderBy(p => { NodeAttribute na = p.GetCustomAttributes(typeof(NodeAttribute), true)[0] as NodeAttribute; int i = na.Index; Console.WriteLine($"{na.Name},{na.Index}"); return i; }).ToArray(); //沒有註解的屬性 var defaultNodeProps = props.Where(p => { var cus = p.GetCustomAttributes(typeof(NodeAttribute), true); return cus == null || cus.Count() <= 0; }); List<PropertyInfo> pis = new List<PropertyInfo>(); //按註解裡的先後順序新增結點 if (selfNodeProps != null && selfNodeProps.Count() > 0) { pis.AddRange(selfNodeProps); } //再新增沒有註解的結點 if (defaultNodeProps != null && defaultNodeProps.Count() > 0) { pis.AddRange(defaultNodeProps); } foreach (var prop in pis) { KeyValueEntity kv = new Entities.KeyValueEntity(); kv.Key = prop.Name;//預設沒有註解,用屬性名 var attrs = prop.GetCustomAttributes(typeof(NodeAttribute), true); if (attrs != null && attrs.Count() > 0) { NodeAttribute attr = attrs[0] as NodeAttribute; if (attr.Name.IsNotNullOrEmpty())//註解裡有結點名 { kv.Key = attr.Name; } } if (nodeFormatter != null)//如果設定了更改規則,按規則修改 { kv.Key = nodeFormatter(kv.Key); } object v = prop.GetValue(data); if (prop.PropertyType.ToString().Contains("DateTime")) { if (v != null) { if (timeFormat.IsNullOrEmpty()) timeFormat = "yyyyMMdd"; kv.Value = Convert.ToDateTime(v).ToString(timeFormat); } } else if (v != null) { kv.Value = Convert.ToString(v); } list.Add(kv); } } return list; } } /// <summary> /// xml文件序列化,反序列化 /// TimeFormat:設定日期屬性的格式 /// NodeNameFormatter:可以對結點名稱格式化 /// 結點名:如果設定了NodeAttr的Name,按設定取,否則預設取屬性名 /// CreateNodeWhenNull:設定屬性值為null時是否生成結點 /// ContainPropertyWithOutNodeAttr:屬性值沒有被NodeAttr標記時,是否生成、解析結點 /// </summary> public class XmlSerializer : XmlNodeSerializer { public new XmlDocument Serialize<T>(T data) { return Serialize(data, "UTF-8"); } public XmlDocument Serialize<T>(T data, string encoding) { if (data == null) return null; XmlDocument doc = new XmlDocument(); XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", encoding, null); doc.AppendChild(dec); XmlNode node = Serialize(data, doc); if (node != null) { doc.AppendChild(node); } return doc; } public T DeSerialize<T>(XmlDocument doc) where T : class { if (doc == null) return null; Type type = typeof(T); string nodeName = string.Empty; //結點名 NodeAttribute na = type.GetCustomAttribute<NodeAttribute>(); if (na == null || na.Name.IsNullOrEmpty()) { nodeName = type.Name;//如果沒有設定,使用類名 } else { nodeName = na.Name;//取設定的結點名 } if (nodeFormatter != null) nodeName = nodeFormatter(nodeName); XmlNode node = doc.SelectSingleNode(nodeName); if (node == null) return null; return DeSerialize<T>(node); //doc.firstchild可能是XmlDeclaration } public T DeSerialize<T>(string xml) where T : class { try { XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); return DeSerialize<T>(doc); } catch (XmlException ex) { Console.WriteLine(ex.Message); return null; } } } public class NodeAttribute : Attribute { /// <summary> /// 結點名稱 /// </summary> public string Name { get; set; } /// <summary> /// 結點在文件內的順序 /// </summary> public int Index { get; set; } = int.MaxValue;//預設最大值,因為有些node只設置了name沒設定index,index就會預設為0就排到第一個去了 }