反射+特性实现 类和XML文档的序列化反序列化
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就排到第一个去了 }