反射+特性实现 类和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就排到第一个去了
    }

 

posted @ 2018-12-24 11:56  chyun2011  阅读(201)  评论(0编辑  收藏  举报