必须填写至少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 XmlReaderX : TextReaderBase<XmlReaderWriterSettings>
    {
        #region 属性
        private XmlReader _Reader;
        /// <summary>读取器</summary>
        public XmlReader Reader
        {
            get
            {
                if (_Reader == null)
                {
                    var settings = new XmlReaderSettings();
                    settings.IgnoreWhitespace = true;
                    settings.IgnoreComments = true;
                    _Reader = XmlReader.Create(Stream, settings);
                }
                return _Reader;
            }
            set
            {
                _Reader = value;

                var xr = _Reader as XmlTextReader;
                if (xr != null && Settings.Encoding != xr.Encoding) Settings.Encoding = xr.Encoding;
            }
        }

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

        /// <summary>获取一个值,该值表示当前的流位置是否在流的末尾。</summary>
        /// <returns>如果当前的流位置在流的末尾,则为 true;否则为 false。</returns>
        public override bool EndOfStream
        {
            get
            {
                var r = Reader;
                if (r != null) return r.EOF;

                var s = Stream;
                if (s != null) return s.Position == s.Length;

                return false;
            }
        }

        /// <summary>初始化</summary>
        public XmlReaderX() { }

        /// <summary>使用xml字符串初始化</summary>
        /// <param name="xml"></param>
        public XmlReaderX(String xml)
        {
            Stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
        }

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

        private String _Lengths;
        /// <summary>多维数组长度</summary>
        public String Lengths { get { return _Lengths; } set { _Lengths = value; } }
        #endregion

        #region 基础元数据
        /// <summary>从当前流中读取一个字符串。字符串有长度前缀,一次 7 位地被编码为整数。</summary>
        /// <returns></returns>
        public override string ReadString()
        {
            Boolean isElement = Reader.NodeType == XmlNodeType.Element;
            if (isElement)
            {
                if (SkipEmpty()) return null;
                Reader.ReadStartElement();
            }

            String str = Reader.ReadContentAsString();
            if (isElement && Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

            WriteLog(1, "ReadString", str);
            return str;
        }
        #endregion

        #region 扩展类型
        /// <summary>读对象类型</summary>
        /// <returns></returns>
        protected override Type OnReadType()
        {
            if (Reader.MoveToAttribute("Type"))
            {
                Type t = base.OnReadType();
                if (Reader.NodeType == XmlNodeType.Attribute) Reader.MoveToElement();
                return t;
            }

            return base.OnReadType();
        }
        #endregion

        #region 字典
        /// <summary>尝试读取字典类型对象</summary>
        /// <param name="type">类型</param>
        /// <param name="value">对象</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        public override bool ReadDictionary(Type type, ref object value, ReadObjectCallback callback)
        {
            if (SkipEmpty()) return true;

            return base.ReadDictionary(type, ref value, callback);
        }

        /// <summary>读取字典项</summary>
        /// <param name="keyType">键类型</param>
        /// <param name="valueType">值类型</param>
        /// <param name="value">字典项</param>
        /// <param name="index">元素序号</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        protected override bool OnReadDictionaryEntry(Type keyType, Type valueType, ref DictionaryEntry value, Int32 index, ReadObjectCallback callback)
        {
            if (Reader.NodeType == XmlNodeType.EndElement) return false;

            Object key = null;
            Object val = null;

            // <Item>
            Reader.ReadStartElement();

            // <Key>
            Reader.ReadStartElement();
            if (!ReadObject(keyType, ref key)) return false;
            // </Key>
            if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

            // <Value>
            if (!ReadObject(valueType, ref val)) return false;
            // </Value>

            value.Key = key;
            value.Value = val;

            // </Item>
            if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

            return true;
        }
        #endregion

        #region 枚举
        /// <summary>尝试读取枚举</summary>
        /// <remarks>重点和难点在于如果得知枚举元素类型,这里假设所有元素类型一致,否则实在无法处理</remarks>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        public override bool ReadEnumerable(Type type, ref object value, ReadObjectCallback callback)
        {
            Type t = type.GetElementType();

            #region 锯齿二维数组处理
            Int32 length = 1;
            while (typeof(IEnumerable).IsAssignableFrom(t))
            {
                length++;
                t = t.GetElementType();
            }

            if (length > 1)
            {
                Array array = TypeX.CreateInstance(type, length) as Array;
                t = type.GetElementType();
                for (int j = 0; j < length - 1; j++)
                {
                    //开始循环之前已赋值,所以第一次循环时跳过
                    if (j > 0) t = t.GetElementType();

                    for (int i = 0; i < array.Length; i++)
                    {
                        if (value != null) value = null;
                        if (base.ReadEnumerable(t, ref value, callback) && value != null)
                        {
                            array.SetValue(value, i);
                        }
                    }
                }
                if (array != null && array.Length > 0) value = array;
                return true;
            }
            #endregion

            return base.ReadEnumerable(type, ref value, callback);
        }

        /// <summary>读取枚举项</summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <param name="index">元素序号</param>
        /// <param name="callback">处理元素的方法</param>
        /// <returns></returns>
        protected override bool OnReadItem(Type type, ref object value, Int32 index, ReadObjectCallback callback)
        {
            String name = type.GetCustomAttributeValue<XmlRootAttribute, String>(true);
            if (String.IsNullOrEmpty(name) && type != null) name = type.Name;

            if (Reader.NodeType == XmlNodeType.EndElement || Reader.Name != name) return false;

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

            if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

            return rs;
        }
        #endregion

        #region 读取对象
        /// <summary>尝试读取目标对象指定成员的值,通过委托方法递归处理成员</summary>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        protected override bool OnReadObject(Type type, ref object value, ReadObjectCallback callback)
        {
            if (Depth > 1) return base.OnReadObject(type, ref value, callback);

            while (Reader.NodeType != XmlNodeType.Element) { if (!Reader.Read())return false; }
            if (String.IsNullOrEmpty(RootName)) RootName = Reader.Name;

            return base.OnReadObject(type, ref value, callback);
        }

        /// <summary>尝试读取引用对象</summary>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        protected override bool ReadRefObject(Type type, ref object value, ReadObjectCallback callback)
        {
            Boolean isElement = Reader.NodeType == XmlNodeType.Element;
            Boolean isAtt = Settings.MemberAsAttribute && XmlWriterX.IsAttributeType(type);
            if (isElement && !isAtt)
            {
                if (SkipEmpty()) return true;
                if (Reader.MoveToAttribute("Lengths"))
                {
                    WriteLog("ReadLengths");
                    Lengths = ReadString();
                    if (Reader.NodeType == XmlNodeType.Attribute) Reader.MoveToElement();
                }
                Reader.ReadStartElement();
            }

            Boolean b = base.ReadRefObject(type, ref value, callback);

            if (isElement && !isAtt && Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

            return b;
        }

        /// <summary>读取成员之前获取要读取的成员,默认是index处的成员,实现者可以重载,改变当前要读取的成员,如果当前成员不在数组里面,则实现者自己跳到下一个可读成员。</summary>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="members">可匹配成员数组</param>
        /// <param name="index">索引</param>
        /// <returns></returns>
        protected override IObjectMemberInfo GetMemberBeforeRead(Type type, object value, IObjectMemberInfo[] members, int index)
        {
            String name = String.Empty;

            while (Reader.NodeType != XmlNodeType.None && Reader.IsStartElement())
            {
                name = Reader.Name;

                IObjectMemberInfo member = GetMemberByName(members, name);
                if (member != null) return member;

                if (SkipEmpty()) continue;

                Reader.ReadStartElement();

                if (!SkipEmpty()) Reader.Read();

                if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();
            }

            return null;
        }

        /// <summary>读取对象成员</summary>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="member">成员</param>
        /// <param name="index">成员索引</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        protected override bool OnReadMember(Type type, ref object value, IObjectMemberInfo member, Int32 index, ReadObjectCallback callback)
        {
            Boolean isAtt = Settings.MemberAsAttribute && XmlWriterX.IsAttributeType(member.Type);
            if (isAtt) Reader.MoveToAttribute(member.Name);

            return base.OnReadMember(type, ref value, member, index, callback);
        }

        /// <summary>读取对象引用计数</summary>
        /// <returns></returns>
        protected override int OnReadObjRefIndex()
        {
            if (Reader.MoveToAttribute("ObjRef"))
            {
                Int32 rs = ReadInt32();

                // 从特性移到元素,方便后续读取操作,如果后续还需要读取特性,则应该自己移到特性处
                if (Reader.NodeType == XmlNodeType.Attribute) Reader.MoveToElement();

                // 跳过空元素,可能该元素已经读取了对象引用
                SkipEmpty();

                return rs;
            }

            return -1;
        }
        #endregion

        #region 未知对象
        /// <summary>读取未知对象(其它所有方法都无法识别的对象),采用BinaryFormatter或者XmlSerialization</summary>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        public override bool ReadUnKnown(Type type, ref object value, ReadObjectCallback callback)
        {
            try
            {
                WriteLog("XmlSerializer", type.Name);
                XmlSerializer serializer = new XmlSerializer(type);
                String str = Reader.ReadString();
                if (!String.IsNullOrEmpty(str))
                {
                    MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(str));
                    value = serializer.Deserialize(ms);
                }
                return true;
            }
            catch
            {
                //只能处理公共类型,Type因其保护级别而不可访问。
            }
            return base.ReadUnKnown(type, ref value, callback);
        }
        #endregion

        #region 方法
        /// <summary>当前节点是否空。如果是空节点,则读一次,让指针移到下一个元素</summary>
        Boolean SkipEmpty()
        {
            // 空元素直接返回
            if (Reader.IsEmptyElement)
            {
                // 读一次,把指针移到下一个元素上
                Reader.Read();
                return true;
            }

            return false;
        }

        /// <summary>读取多维数组相关参数</summary>
        /// <returns></returns>
        protected override string ReadLengths() { return Lengths; }

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

            return dic;
        }

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

            return dic;
        }
        #endregion

        #region 序列化接口
        /// <summary>读取实现了可序列化接口的对象</summary>
        /// <param name="type">要读取的对象类型</param>
        /// <param name="value">要读取的对象</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否读取成功</returns>
        public override bool ReadSerializable(Type type, ref object value, ReadObjectCallback callback)
        {
            if (!typeof(IXmlSerializable).IsAssignableFrom(type))
                return base.ReadSerializable(type, ref value, callback);

            if (value == null) value = TypeX.CreateInstance(type);
            ((IXmlSerializable)value).ReadXml(Reader);
            return true;
        }
        #endregion
    }
}