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

namespace NewLife.Serialization
{
    /// <summary>Json写入器</summary>
    public class JsonWriter : TextWriterBase<JsonSettings>
    {
        #region 属性
        private TextWriter _Writer;
        /// <summary>写入器</summary>
        public TextWriter Writer
        {
            get { return _Writer ?? (_Writer = new StreamWriter(Stream, Settings.Encoding)); }
            set
            {
                _Writer = value;
                if (Settings.Encoding != _Writer.Encoding) Settings.Encoding = _Writer.Encoding;

                var sw = _Writer as StreamWriter;
                if (sw != null && sw.BaseStream != Stream) Stream = sw.BaseStream;
            }
        }

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

        #region 构造方法
        /// <summary>构造方法</summary>
        public JsonWriter()
            : base()
        {
            Settings.DepthLimit = 16;
#if DEBUG
            Settings.DepthLimit = 5;
#endif
        }
        #endregion

        #region 字节/字节数组
        /// <summary>将字节数组部分写入当前流。</summary>
        /// <param name="buffer">包含要写入的数据的字节数组。</param>
        /// <param name="index">buffer 中开始写入的起始点。</param>
        /// <param name="count">要写入的字节数。</param>
        public override void Write(byte[] buffer, int index, int count) { WriteEnumerable(Slice(buffer, index, count), typeof(Byte[]), null); }
        #endregion

        #region 时间
        /// <summary>将一个时间日期写入</summary>
        /// <param name="value"></param>
        public override void Write(DateTime value)
        {
            Depth++;
            WriteLog("WriteValue", "DateTime", value);

            if (Settings.JsonDateTimeKind != DateTimeKind.Unspecified && value.Kind != Settings.JsonDateTimeKind)
            {
                if (Settings.JsonDateTimeKind == DateTimeKind.Local)
                    value = value.ToLocalTime();
                else
                    value = value.ToUniversalTime();
            }

            switch (Settings.JsonDateTimeFormat)
            {
                case JsonDateTimeWriteFormat.ISO8601:
                    Write(value.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture));
                    break;
                case JsonDateTimeWriteFormat.DotnetDateTick:
                    Write(string.Format("\\/Date({0})\\/", (long)(value - Settings.BaseDateTime).TotalMilliseconds));
                    break;
                case JsonDateTimeWriteFormat.Tick:
                    Write(Settings.ConvertDateTimeToInt64(value));
                    break;
            }
            Depth--;
        }
        #endregion

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

        /// <summary>输出空</summary>
        protected override void WriteNull() { WriteLiteral("null"); }

        void WriteLine() { if (Settings.AllowMultiline) Writer.WriteLine(); }

        /// <summary>将 Unicode 字符写入当前流,并根据所使用的 Encoding 和向流中写入的特定字符,提升流的当前位置。</summary>
        /// <param name="ch">要写入的非代理项 Unicode 字符。</param>
        public override void Write(char ch)
        {
            if (ch == '\0')
                WriteNull();
            else
                Write(ch.ToString());
        }

        /// <summary>将 Unicode 字符写入当前流,并根据所使用的 Encoding 和向流中写入的特定字符,提升流的当前位置。</summary>
        /// <param name="chars"></param>
        public override void Write(char[] chars)
        {
            if (chars == null)
                WriteNull();
            else
                Write(chars, 0, chars.Length);
        }

        /// <summary>将 Unicode 字符写入当前流,并根据所使用的 Encoding 和向流中写入的特定字符,提升流的当前位置。</summary>
        /// <param name="chars"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        public override void Write(char[] chars, int index, int count)
        {
            if (Settings.UseCharsWriteToString)
                Write(new String(chars));
            else
                WriteEnumerable(Slice(chars, index, count), typeof(char[]), null);
        }

        /// <summary>写入字符串</summary>
        /// <param name="value">要写入的值。</param>
        public override void Write(string value)
        {
            value = JavascriptStringEncode(value, this.Settings.UseStringUnicodeEncode);
            Depth++;
            WriteLog("WriteValue", "String", value);
            Writer.Write("\"" + value + "\"");
            Depth--;
        }

        /// <summary>将指定字符串编码成json中表示的字符串,将编码Unicode字符为\uXXXX</summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string JavascriptStringEncode(string value) { return JavascriptStringEncode(value, true); }

        /// <summary>将指定字符串编码成javascript的字面字符串(即写入到js代码中的和value内容相同的代码),开始和结尾不包含双引号</summary>
        /// <param name="value">要编码的字符串,value为null时返回""</param>
        /// <param name="encodeUnicode">是否将Unicode字符编码为\uXXXX的格式</param>
        /// <returns></returns>
        public static string JavascriptStringEncode(string value, bool encodeUnicode)
        {
            if (string.IsNullOrEmpty(value)) return string.Empty;

            StringBuilder builder = null;
            int startIndex = 0;
            int count = 0;
            for (int i = 0; i < value.Length; i++)
            {
                char c = value[i];
                string estr = null;
                // 拥有特殊字符时才编码处理
                switch (c) //根据json.org定义的string规范
                {
                    case '"':
                        estr = "\\\""; break;
                    case '\\':
                        estr = "\\\\"; break;
                    case '/':
                        estr = "/"; break;
                    case '\b':
                        estr = "\\b"; break;
                    case '\f':
                        estr = "\\f"; break;
                    case '\n':
                        estr = "\\n"; break;
                    case '\r':
                        estr = "\\r"; break;
                    case '\t':
                        estr = "\\t"; break;
                    default:
                        if (c < ' ' || (encodeUnicode && c > 0x7e)) // 避免json直接输出中文乱码的情况
                        {
                            estr = string.Format("\\u{0:x4}", ((UInt16)c));
                        }
                        break;
                }
                if (estr != null)
                {
                    if (builder == null) builder = new StringBuilder(value.Length + 5);

                    if (count > 0) builder.Append(value, startIndex, count);

                    startIndex = i + 1;

                    count = 0;
                    builder.Append(estr);
                }
                else
                {
                    count++;
                }
            }
            if (builder == null) return value;
            if (count > 0) builder.Append(value, startIndex, count);
            return builder.ToString();
        }
        #endregion

        #region 枚举
        /// <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)
        {
            Boolean rs;
            Writer.Write("[");
            ComplexObjectDepth++;
            if (!ComplexObjectDepthIsOverflow())
            {
                if (type.IsArray && value.GetType().GetArrayRank() > 1)
                {
                    Array array = value as Array;
                    List<String> lengths = new List<String>();
                    for (int i = 0; i < array.Rank; i++)
                    {
                        lengths.Add(array.GetLength(i).ToString());
                    }
                    WriteLengths(String.Join(",", lengths.ToArray()));
                    Writer.Write(",");
                }
                rs = base.WriteEnumerable(value, type, callback);
            }
            else
            {
                Depth++;
                WriteLog("WriteSkip", "ComplexObjectDepthIsOverflow");
                Depth--;
                rs = true;
            }
            ComplexObjectDepth--;
            Writer.Write("]");

            return rs;
        }

        /// <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, int index, WriteObjectCallback callback)
        {
            WriteLog("WriteEnumerableItem", index, type != null ? type.FullName : "Unknown type");
            if (index > 0) Writer.Write(",");

            if (value != null && !IsExactType(type))
            {
                type = value.GetType(); //避免base.OnWriteItem中写入value.GetType()
                writeValueType = value;
            }
            bool ret;
            if (value == null)
            {
                WriteNull();
                ret = true;
            }
            else
            {
                ret = base.OnWriteItem(value, type, index, callback);
            }
            writeValueType = null;
            return ret;
        }

        /// <summary>返回指定数组的一个片段,始终返回的是array参数的一个副本</summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="array"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static T[] Slice<T>(T[] array, int index, int count)
        {
            T[] ret = new T[count];
            if (count > 0) Array.Copy(array, index, ret, 0, count);

            return ret;
        }
        #endregion

        #region 字典
        /// <summary>将字典类型数据写入到当前流位置</summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public override bool WriteDictionary(IDictionary value, Type type, WriteObjectCallback callback)
        {
            if (value == null)
            {
                WriteNull();
                return true;
            }
            bool ret;
            Writer.Write("{");

            ComplexObjectDepth++;
            if (!ComplexObjectDepthIsOverflow())
            {
                WriteLine();
                ret = base.WriteDictionary(value, type, callback);
                WriteLine();
            }
            else
            {
                Depth++;
                WriteLog("WriteSkip", "ComplexObjectDepthIsOverflow");
                Depth--;
                ret = true;
            }
            ComplexObjectDepth--;
            Writer.Write("}");
            return ret;
        }

        /// <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)
        {
            if (index > 0)
            {
                Writer.Write(",");
                WriteLine();
            }
            WriteLog("WriteDictionaryEntry", "Key");
            Write(value.Key.ToString());//json标准要求key必须是字符串
            Writer.Write(":");
            WriteLog("WriteDictionaryEntry", "Value");
            if (value.Value != null && !IsExactType(valueType)) //无法取得字典项的值类型
            {
                writeValueType = value.Value; //valueType会在WriteObject内部被重新赋值,所以不做额外处理
            }
            bool ret = WriteObject(value.Value, valueType, callback);
            writeValueType = null;
            return ret;
        }
        #endregion

        #region 写入对象
        /// <summary>是否需要写入值类型信息的标志,为null时表示不需要,非null时并且等于待写入的值时写入值类型</summary>
        object writeValueType = null;
        /// <summary>写入的复合对象深度,指使用{} []包括的深度</summary>
        int ComplexObjectDepth = 0;
        /// <summary>是否写入成员的计数器,用于控制换行输出</summary>
        int WriteMemberCount = 0;

        /// <summary>JsonWriter的对象类型由writeValueType写入,作为第一个成员,所以不需要</summary>
        /// <param name="type"></param>
        protected override void WriteObjectType(Type type) { }

        /// <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 (value == null)
            {
                WriteNull();
                return true;
            }
            else if (!IsExactType(type))
            {
                type = value.GetType();
                if (Depth == 1) writeValueType = value;
            }
            if (Type.GetTypeCode(type) == TypeCode.Int16) // 在基类WriteValue时 Int16将会被转换成Int32处理,所以这里需要针对Int16特殊处理
            {
                Int32 v = 0;
                if (value != null) v = Convert.ToInt32(value.ToString());

                value = v;
                type = typeof(Int32);
            }
            return base.OnWriteObject(value, type, callback);
        }

        /// <summary>写对象成员</summary>
        /// <param name="value">要写入的对象</param>
        /// <param name="type">要写入的对象类型</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否写入成功</returns>
        public override bool WriteCustomObject(object value, Type type, WriteObjectCallback callback)
        {
            Boolean rs, writedType = false;
            Writer.Write("{");
            if (value != null && writeValueType == value) //写入明确的类型
            {
                WriteLine();
                string fullname = value.GetType().FullName;
                Depth++;
                WriteLog("WriteType", "__type", fullname);
                WriteLiteral(string.Format("\"__type\":\"{0}\"", JavascriptStringEncode(fullname, this.Settings.UseStringUnicodeEncode)));
                //后续的逗号和换行符由WriteCustomObject中OnWriteMember输出,并将writeValueType置为null 因为后续可能没有任何成员
                Depth--;
                writedType = true;
            }

            ComplexObjectDepth++;
            if (!ComplexObjectDepthIsOverflow())
            {
                int i = WriteMemberCount;
                rs = base.WriteCustomObject(value, type, callback);
                writeValueType = null;
                if (WriteMemberCount > i)
                {
                    WriteLine();
                }
                WriteMemberCount = i;
            }
            else
            {
                if (writedType) WriteLine();
                Depth++;
                WriteLog("WriteSkip", "ComplexObjectDepthIsOverflow");
                Depth--;
                rs = true;
            }
            ComplexObjectDepth--;
            Writer.Write("}");

            return rs;
        }

        /// <summary>写入成员</summary>
        /// <param name="value">要写入的对象</param>
        /// <param name="memberType">要写入的成员类型</param>
        /// <param name="member">要写入的成员信息,可以通过[value]取得成员值</param>
        /// <param name="index">成员索引</param>
        /// <param name="callback">处理成员的方法</param>
        /// <returns>是否写入成功</returns>
        protected override bool OnWriteMember(object value, Type memberType, IObjectMemberInfo member, Int32 index, WriteObjectCallback callback)
        {
            if (index > 0 || writeValueType != null)
            {
                writeValueType = null;
                Writer.Write(",");
            }
            WriteMemberCount++;

            WriteLine();

            Writer.Write("\"" + JavascriptStringEncode(member.Name) + "\":");

            object obj = member[value];
            if (obj != null && !IsExactType(memberType))
            {
                memberType = obj.GetType(); //避免base.OnWriteMember中写入obj.GetType()
                writeValueType = obj;
            }
            bool ret = base.OnWriteMember(value, memberType, member, index, callback);
            writeValueType = null;
            return ret;
        }

        /// <summary>当前解析复合对象深度是否超出,用于避免循环引用可能引起的堆栈溢出,仅在Settings.RepeatedActionType是RepeatedAction.DepthLimit时才可能返回true</summary>
        /// <returns></returns>
        public bool ComplexObjectDepthIsOverflow()
        {
            return Settings.DuplicatedObjectWriteMode == DuplicatedObjectWriteMode.DepthLimit && ComplexObjectDepth > Settings.DepthLimit;
        }
        #endregion

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

            base.Flush();
        }

        /// <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 TextWriter;

            return dic;
        }
        #endregion
    }
}