必须填写至少10个字的日志
nnhy 编写于 2012-07-27 18:48:21
X
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using NewLife.Reflection;
using NewLife.Serialization;

namespace NewLife.Xml
{
    /// <summary>Xml写入器</summary>
    public class XmlWriterX : TextWriterBase<XmlReaderWriterSettings>
    {
        #region 属性
        private XmlWriter _Writer;
        /// <summary>写入器</summary>
        public XmlWriter Writer
        {
            get
            {
                if (_Writer == null)
                {
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Encoding = Settings.Encoding;
                    settings.Indent = true;
                    _Writer = XmlWriter.Create(Stream, settings);
                }
                return _Writer;
            }
            set
            {
                _Writer = value;
                if (Settings.Encoding != _Writer.Settings.Encoding) Settings.Encoding = _Writer.Settings.Encoding;

                XmlTextWriter xw = _Writer as XmlTextWriter;
                if (xw != null && Stream != xw.BaseStream) Stream = xw.BaseStream;
            }
        }

        /// <summary>数据流。更改数据流后,重置Writer为空,以使用新的数据流</summary>
        public override Stream Stream
        {
            get { return base.Stream; }
            set
            {
                if (base.Stream != value) _Writer = null;
                base.Stream = value;
            }
        }

        private String _RootName;
        /// <summary>根元素名</summary>
        public String RootName { get { return _RootName; } set { _RootName = value; } }
        #endregion

        #region 基础元数据
        #region 字符串
        /// <summary>输出字符串字面值,不做编码处理</summary>
        protected override void OnWriteLiteral(string value)
        {
            Writer.WriteString(value);

            AutoFlush();
        }

        ///// <summary>
        ///// 写入字符串
        ///// </summary>
        ///// <param name="value">要写入的值。</param>
        //public override void Write(string value)
        //{
        //    Writer.WriteString(value);

        //    AutoFlush();
        //}
        #endregion
        #endregion

        #region 扩展类型
        /// <summary>写对象类型</summary>
        /// <param name="type"></param>
        protected override void WriteObjectType(Type type) { if (Settings.WriteType) Writer.WriteAttributeString("Type", type.FullName); }
        #endregion

        #region 字典
        /// <summary>写入字典项 </summary>
        /// <param name="value">对象</param>
        /// <param name="keyType">键类型</param>
        /// <param name="valueType">值类型</param>
        /// <param name="index">成员索引</param>
        /// <param name="callback">使用指定委托方法处理复杂数据</param>
        /// <returns>是否写入成功</returns>
        protected override bool OnWriteKeyValue(DictionaryEntry value, Type keyType, Type valueType, int index, WriteObjectCallback callback)
        {
            // 如果无法取得字典项类型,则每个键值都单独写入类型
            Writer.WriteStartElement("Item");

            {
                Writer.WriteStartElement("Key");
                if (!WriteObject(value.Key, keyType, callback)) return false;
                Writer.WriteEndElement();
            }

            {
                Writer.WriteStartElement("Value");
                if (!WriteObject(value.Value, valueType, callback)) return false;
                Writer.WriteEndElement();
            }

            Writer.WriteEndElement();

            return true;
        }
        #endregion

        #region 枚举
        /// <summary>写入枚举项</summary>
        /// <param name="value">对象</param>
        /// <param name="type">类型</param>
        /// <param name="index">成员索引</param>
        /// <param name="callback">使用指定委托方法处理复杂数据</param>
        /// <returns>是否写入成功</returns>
        protected override bool OnWriteItem(Object value, Type type, Int32 index, WriteObjectCallback callback)
        {
            Type t = type;
            if (value != null) t = value.GetType();
            String name = t.GetCustomAttributeValue<XmlRootAttribute, String>(true);
            if (String.IsNullOrEmpty(name) && t != null) name = t.Name;

            Writer.WriteStartElement(name);

            AutoFlush();

            Boolean rs = base.OnWriteItem(value, type, index, callback);

            AutoFlush();

            Writer.WriteEndElement();

            AutoFlush();

            return rs;
        }

        /// <summary>写入枚举数据,复杂类型使用委托方法进行处理</summary>
        /// <param name="value">对象</param>
        /// <param name="type">类型</param>
        /// <param name="callback">使用指定委托方法处理复杂数据</param>
        /// <returns>是否写入成功</returns>
        public override bool WriteEnumerable(IEnumerable value, Type type, WriteObjectCallback callback)
        {
            if (value == null) return true;

            Type t = value.GetType();
            Type elementType = null;
            if (t.HasElementType) elementType = t.GetElementType();
            Boolean result = false;
            if (typeof(IEnumerable).IsAssignableFrom(elementType))
            {
                if (typeof(IEnumerable).IsAssignableFrom(elementType.GetElementType()))
                {
                    elementType = elementType.GetElementType();
                    WriteEnumerable(value as IEnumerable, elementType, callback);
                }
                foreach (Object item in value)
                {
                    WriteLog("WriteEnumerable", elementType.Name);
                    Writer.WriteStartElement("Item");
                    result = base.WriteEnumerable(item as IEnumerable, elementType, callback);
                    Writer.WriteEndElement();
                }
                return result;
            }

            if (value.GetType().IsArray && value.GetType().GetArrayRank() > 1)
            {
                Array arr = value as Array;
                List<String> lengths = new List<String>();
                for (int j = 0; j < value.GetType().GetArrayRank(); j++)
                {
                    lengths.Add(arr.GetLength(j).ToString());
                }
                WriteLengths(String.Join(",", lengths.ToArray()));
            }

            return base.WriteEnumerable(value, type, callback);
        }
        #endregion

        #region 写入对象
        /// <summary>已重载。写入文档的开头和结尾</summary>
        /// <param name="value">要写入的对象</param>
        /// <param name="type">要写入的对象类型</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否写入成功</returns>
        protected override bool OnWriteObject(object value, Type type, WriteObjectCallback callback)
        {
            if (Depth > 1) return base.OnWriteObject(value, type, callback);

            Type t = type;
            if (t == null && value != null) t = value.GetType();
            String name = RootName;

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

                    if (String.IsNullOrEmpty(RootName)) RootName = name;
                }
            }

            if (Depth == 1) Writer.WriteStartDocument();
            Writer.WriteStartElement(name);

            AutoFlush();

            Boolean rs = base.OnWriteObject(value, type, callback);

            AutoFlush();

            if (Writer.WriteState != WriteState.Start)
            {
                Writer.WriteEndElement();
                if (Depth == 1) Writer.WriteEndDocument();
            }
            AutoFlush();

            return rs;
        }

        /// <summary>写入对象成员</summary>
        /// <param name="value">要写入的对象</param>
        /// <param name="type">要写入的对象类型</param>
        /// <param name="member">成员</param>
        /// <param name="index">成员索引</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否写入成功</returns>
        protected override bool OnWriteMember(object value, Type type, IObjectMemberInfo member, Int32 index, WriteObjectCallback callback)
        {
            // 检查成员的值,如果是默认值,则不输出
            if (value != null && Settings.IgnoreDefault && IsDefault(value, member)) return true;

            // 特殊处理特性,只有普通值类型才能输出为特性
            Boolean isAtt = Settings.MemberAsAttribute && IsAttributeType(member.Type);
            if (isAtt)
                Writer.WriteStartAttribute(member.Name);
            else
                Writer.WriteStartElement(member.Name);

            AutoFlush();

            Boolean rs = base.OnWriteMember(value, type, member, index, callback);

            if (!isAtt) Writer.WriteEndElement();

            AutoFlush();

            return rs;
        }

        /// <summary>写对象引用计数</summary>
        /// <param name="index"></param>
        protected override void OnWriteObjRefIndex(int index) { if (index > 0) Writer.WriteAttributeString("ObjRef", index.ToString()); }
        #endregion

        #region 未知对象
        /// <summary>写入未知对象(其它所有方法都无法识别的对象),采用BinaryFormatter或者XmlSerialization</summary>
        /// <param name="value">要写入的对象</param>
        /// <param name="type">要写入的对象类型</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否写入成功</returns>
        public override bool WriteUnKnown(object value, Type type, WriteObjectCallback callback)
        {
            try
            {
                WriteLog("WriteUnKnown", type.Name);
                XmlSerializer serial = new XmlSerializer(type);
                MemoryStream ms = new MemoryStream();
                serial.Serialize(ms, value);

                String xml = Encoding.UTF8.GetString(ms.ToArray());
                Write(xml);

                return true;
            }
            catch
            {
                //只能处理公共类型,Type因其保护级别而不可访问。
            }
            return base.WriteUnKnown(value, type, callback);
        }
        #endregion

        #region 方法
        /// <summary>刷新缓存中的数据</summary>
        public override void Flush()
        {
            Writer.Flush();

            base.Flush();
        }

        /// <summary>写入长度。多维数组用</summary>
        /// <param name="lengths"></param>
        protected override void WriteLengths(string lengths) { Writer.WriteAttributeString("Lengths", lengths); }

        /// <summary>备份当前环境,用于临时切换数据流等</summary>
        /// <returns>本次备份项集合</returns>
        public override IDictionary<String, Object> Backup()
        {
            var dic = base.Backup();
            dic["Writer"] = Writer;

            return dic;
        }

        /// <summary>恢复最近一次备份</summary>
        /// <returns>本次还原项集合</returns>
        public override IDictionary<String, Object> Restore()
        {
            var dic = base.Restore();
            Writer = dic["Writer"] as XmlWriter;

            return dic;
        }
        #endregion

        #region 序列化接口
        /// <summary>写入实现了可序列化接口的对象</summary>
        /// <param name="value">要写入的对象</param>
        /// <param name="type">要写入的对象类型,如果type等于DataTable,需设置DataTable的名称</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否写入成功</returns>
        public override bool WriteSerializable(object value, Type type, WriteObjectCallback callback)
        {
            if (!typeof(IXmlSerializable).IsAssignableFrom(type)) return base.WriteSerializable(value, type, callback);

            try
            {
                IXmlSerializable xml = value as IXmlSerializable;
                // 这里必须额外写一对标记,否则读取的时候只能读取得到模式而得不到数据
                Boolean b = xml.GetSchema() != null;
                if (b) Writer.WriteStartElement("Data");
                xml.WriteXml(Writer);
                if (b) Writer.WriteEndElement();

                return true;
            }
            catch
            {
                return base.WriteSerializable(value, type, callback);
            }
        }
        #endregion

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

            String name = TypeX.Create(type).Name;
            name = name.Replace("<", "_");
            //name = name.Replace(">", "_");
            name = name.Replace(",", "_");
            name = name.Replace(">", "");
            return name;
        }

        /// <summary>是否可以作为属性写入Xml的类型</summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static Boolean IsAttributeType(Type type)
        {
            if (typeof(Type).IsAssignableFrom(type)) return true;

            TypeCode code = Type.GetTypeCode(type);
            return code != TypeCode.Object;
        }
        #endregion
    }
}