zoukankan      html  css  js  c++  java
  • 反射+特性实现 类和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就排到第一个去了
        }
  • 相关阅读:
    CF359B Permutation
    CF859C Pie Rules
    Contest 156
    Contest 155
    Range Module
    python-环境
    Git 使用
    Contest 154
    生命是一种长期而持续的累积过程
    Contest 153
  • 原文地址:https://www.cnblogs.com/cy2011/p/10167866.html
Copyright © 2011-2022 走看看