写特性时忽略注释
大石头 authored at 2019-04-07 12:18:05
10.25 KiB
X
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using NewLife.Reflection;

namespace NewLife.Serialization
{
    /// <summary>Xml序列化</summary>
    public class Xml : FormatterBase, IXml
    {
        #region 属性
        /// <summary>深度</summary>
        public Int32 Depth { get; set; }

        /// <summary>处理器列表</summary>
        public List<IXmlHandler> Handlers { get; }

        /// <summary>使用特性</summary>
        public Boolean UseAttribute { get; set; }

        /// <summary>使用注释</summary>
        public Boolean UseComment { get; set; }

        /// <summary>当前名称</summary>
        public String CurrentName { get; set; }
        #endregion

        #region 构造
        /// <summary>实例化</summary>
        public Xml()
        {
            // 遍历所有处理器实现
            var list = new List<IXmlHandler>
            {
                new XmlGeneral { Host = this },
                new XmlList { Host = this },
                new XmlComposite { Host = this }
            };
            // 根据优先级排序
            list.Sort();

            Handlers = list;
        }
        #endregion

        #region 处理器
        /// <summary>添加处理器</summary>
        /// <param name="handler"></param>
        /// <returns></returns>
        public Xml AddHandler(IXmlHandler handler)
        {
            if (handler != null)
            {
                handler.Host = this;
                Handlers.Add(handler);
                // 根据优先级排序
                Handlers.Sort();
            }

            return this;
        }

        /// <summary>添加处理器</summary>
        /// <typeparam name="THandler"></typeparam>
        /// <param name="priority"></param>
        /// <returns></returns>
        public Xml AddHandler<THandler>(Int32 priority = 0) where THandler : IXmlHandler, new()
        {
            var handler = new THandler
            {
                Host = this
            };
            if (priority != 0) handler.Priority = priority;

            return AddHandler(handler);
        }
        #endregion

        #region 写入
        /// <summary>写入一个对象</summary>
        /// <param name="value">目标对象</param>
        /// <param name="name">名称</param>
        /// <param name="type">类型</param>
        /// <returns></returns>
        public Boolean Write(Object value, String name = null, Type type = null)
        {
            if (type == null)
            {
                if (value == null) return true;

                type = value.GetType();
            }

            var writer = GetWriter();

            // 检查接口
            if (value is IXmlSerializable)
            {
                (value as IXmlSerializable).WriteXml(writer);
                return true;
            }

            if (String.IsNullOrEmpty(name))
            {
                // 优先采用类型上的XmlRoot特性
                name = type.GetCustomAttributeValue<XmlRootAttribute, String>(true);
                if (String.IsNullOrEmpty(name)) name = GetName(type);
            }

            name = name.Replace('<', '_');
            name = name.Replace('>', '_');
            name = name.Replace('`', '_');
            CurrentName = name;

            // 一般类型为空是顶级调用
            if (Hosts.Count == 0 && Log != null && Log.Enable) WriteLog("XmlWrite {0} {1}", name ?? type.Name, value);

            // 要先写入根
            Depth++;
            if (Depth == 1) writer.WriteStartDocument();

            WriteStart(type);
            try
            {
                foreach (var item in Handlers)
                {
                    if (item.Write(value, type)) return true;
                }

                writer.WriteValue(value);

                return false;
            }
            finally
            {
                WriteEnd();
                if (Depth == 1)
                {
                    writer.WriteEndDocument();
                    writer.Flush();
                }
                Depth--;
            }
        }

        Boolean IFormatterX.Write(Object value, Type type) => Write(value, null, type);

        /// <summary>写入开头</summary>
        /// <param name="type"></param>
        public void WriteStart(Type type)
        {
            var att = UseAttribute;
            if (!att && Member?.GetCustomAttribute<XmlAttributeAttribute>() != null) att = true;
            if (att && !type.IsValueType && type.GetTypeCode() == TypeCode.Object) att = false;

            var writer = GetWriter();

            // 写入注释。写特性时忽略注释
            if (UseComment && !att)
            {
                var des = "";
                if (Member != null) des = Member.GetDisplayName() ?? Member.GetDescription();
                if (des.IsNullOrEmpty() && type != null) des = type.GetDisplayName() ?? type.GetDescription();

                if (!des.IsNullOrEmpty()) writer.WriteComment(des);
            }

            var name = CurrentName;
            if (att)
                writer.WriteStartAttribute(name);
            else
                writer.WriteStartElement(name);
        }

        /// <summary>写入结尾</summary>
        public void WriteEnd()
        {
            var writer = GetWriter();

            if (writer.WriteState != WriteState.Start)
            {
                if (writer.WriteState == WriteState.Attribute)
                    writer.WriteEndAttribute();
                else
                {
                    writer.WriteEndElement();
                    //替换成WriteFullEndElement方法,写入完整的结束标记。解决读取空节点(短结束标记"/ >")发生错误。
                    //writer.WriteFullEndElement();
                }
            }
        }

        private XmlWriter _Writer;
        /// <summary>获取Xml写入器</summary>
        /// <returns></returns>
        public XmlWriter GetWriter()
        {
            if (_Writer == null)
            {
                var set = new XmlWriterSettings
                {
                    //set.Encoding = Encoding.TrimPreamble();
                    Encoding = Encoding,
                    Indent = true
                };

                _Writer = XmlWriter.Create(Stream, set);
            }

            return _Writer;
        }
        #endregion

        #region 读取
        /// <summary>读取指定类型对象</summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public Object Read(Type type)
        {
            var value = type.CreateInstance();
            if (!TryRead(type, ref value)) throw new Exception("读取失败!");

            return value;
        }

        /// <summary>读取指定类型对象</summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Read<T>() => (T)Read(typeof(T));

        /// <summary>尝试读取指定类型对象</summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Boolean TryRead(Type type, ref Object value)
        {
            var reader = GetReader();
            // 移动到第一个元素
            while (reader.NodeType != XmlNodeType.Element) { if (!reader.Read()) return false; }

            if (Hosts.Count == 0 && Log != null && Log.Enable) WriteLog("XmlRead {0} {1}", type.Name, value);

            // 要先写入根
            Depth++;

            var d = reader.Depth;
            ReadStart(type);

            try
            {
                // 如果读取器层级没有递增,说明这是空节点,需要跳过
                if (reader.Depth == d + 1)
                {
                    foreach (var item in Handlers)
                    {
                        if (item.TryRead(type, ref value)) return true;
                    }

                    value = reader.ReadContentAs(type, null);
                }
            }
            finally
            {
                ReadEnd();
                Depth--;
            }

            return true;
        }

        /// <summary>读取开始</summary>
        /// <param name="type"></param>
        public void ReadStart(Type type)
        {
            var att = UseAttribute;
            if (!att && Member?.GetCustomAttribute<XmlAttributeAttribute>() != null) att = true;

            var reader = GetReader();
            while (reader.NodeType == XmlNodeType.Comment) reader.Skip();

            CurrentName = reader.Name;
            if (reader.HasAttributes)
                reader.MoveToFirstAttribute();
            else
                reader.ReadStartElement();

            while (reader.NodeType == XmlNodeType.Comment || reader.NodeType == XmlNodeType.Whitespace) reader.Skip();
        }

        /// <summary>读取结束</summary>
        public void ReadEnd()
        {
            var reader = GetReader();
            if (reader.NodeType == XmlNodeType.Attribute) reader.Read();
            if (reader.NodeType == XmlNodeType.EndElement) reader.ReadEndElement();
        }

        private XmlReader _Reader;
        /// <summary>获取Xml读取器</summary>
        /// <returns></returns>
        public XmlReader GetReader()
        {
            if (_Reader == null) _Reader = XmlReader.Create(Stream);

            return _Reader;
        }
        #endregion

        #region 辅助方法
        static String GetName(Type type)
        {
            if (type.HasElementType) return "ArrayOf" + GetName(type.GetElementType());

            var name = type.GetName();
            name = name.Replace("<", "_");
            //name = name.Replace(">", "_");
            name = name.Replace(",", "_");
            name = name.Replace(">", "");
            return name;
        }

        /// <summary>获取字符串</summary>
        /// <returns></returns>
        public String GetString() => GetBytes().ToStr(Encoding);
        #endregion
    }
}