com.alibaba.fastjson 序列化 反序列

fastjson 序列化反序列目录 fastjson 序列化反序列序列化SerializeWriter成员变量一 SerializeWriter成员函数1 序列化整形数字2 序列化长整形数字3 序列化浮点类型数字4 序列化...
摘要由CSDN通过智能技术生成

                                  fastjson  序列化 反序列

目录

                                  fastjson  序列化 反序列

在这里插入图片描述

序列化

SerializeWriter成员变量

一 SerializeWriter成员函数

1 序列化整形数字

2 序列化长整形数字

3 序列化浮点类型数字

4 序列化枚举类型

5 序列化单字符

6 序列化Null

7 序列化Boolean

8 序列化16进制字节数组

9 序列化byte字节数组

10 序列化字符串

11 序列化字符数组

12 序列化列表字符串

13 序列化包含特殊字符字符串

14 序列化字段名称

15 序列化Boolean类型字段键值对

16 序列化Int类型字段键值对

二 JSON成员函数

概要

JSON成员函数

序列化回调接口

序列化回调接口实现分析

内部注册的序列化

1 BooleanCodec序列化

2 CharacterCodec序列化

3 IntegerCodec序列化

4 LongCodec序列化

5 FloatCodec序列化

6 BigDecimalCodec序列化

7 BigIntegerCodec序列化

8 StringCodec序列化

9 PrimitiveArraySerializer序列化

10 ObjectArrayCodec序列化

11 MiscCodec序列化

12 AppendableSerializer序列化

13 ToStringSerializer序列化

14 AtomicCodec序列化

15 ReferenceCodec序列化

16 CollectionCodec序列化

 特定序列化实现解析

 1 MapSerializer序列化

2 ListSerializer序列化

3 DateCodec序列化

4 JavaBeanSerializer序列化

反序列化

反序列化回调接口实现分析

内部注册的反序列化

1 BooleanCodec反序列化

2 CharacterCodec反序列化

3 IntegerCodec反序列化

4 LongCodec反序列化

5 FloatCodec反序列化

6  BigDecimalCodec反序列化

 7 StringCodec反序列化

8 ObjectArrayCodec反序列化

9 JavaBeanDeserializer反序列化


序列化

fastjson核心功能包括序列化和反序列化,序列化的含义是将java对象转换成跨语言的json字符串。我认为从这里作为分析入口相对比较简单,

SerializeWriter成员变量

com.alibaba.fastjson.serializer.SerializeWriter类非常重要,序列化输出都是通过转换底层操作,重要字段如下:

    /** 字符类型buffer */
    private final static ThreadLocal<char[]> bufLocal      = new ThreadLocal<char[]>();
    /** 字节类型buffer */
    private final static ThreadLocal<byte[]> bytesBufLocal = new ThreadLocal<byte[]>();

    /** 存储序列化结果buffer */
    protected char                           buf[];

    /** buffer中包含的字符数 */
    protected int                            count;

    /** 序列化的特性,比如写枚举按照名字还是枚举值 */
    protected int                            features;

    /** 序列化输出器 */
    private final Writer                     writer;

    /** 是否使用单引号输出json */
    protected boolean                        useSingleQuotes;
    /** 输出字段是否追加 "和:字符 */
    protected boolean                        quoteFieldNames;
    /** 是否对字段排序 */
    protected boolean                        sortField;
    /** 禁用字段循环引用探测 */
    protected boolean                        disableCircularReferenceDetect;
    protected boolean                        beanToArray;
    /** 按照toString方式获取对象字面值 */
    protected boolean                        writeNonStringValueAsString;
    /** 如果字段默认值不输出,比如原型int,默认值0不输出 */
    protected boolean                        notWriteDefaultValue;
    /** 序列化枚举时使用枚举name */
    protected boolean                        writeEnumUsingName;
    /** 序列化枚举时使用枚举toString值 */
    protected boolean                        writeEnumUsingToString;
    protected boolean                        writeDirect;
    /** key分隔符,默认单引号是',双引号是" */
    protected char                           keySeperator;

    protected int                            maxBufSize = -1;

    protected boolean                        browserSecure;
    protected long                           sepcialBits;

一 SerializeWriter成员函数

1 序列化整形数字

    public void writeInt(int i) {
        /** 如果是整数最小值,调用字符串函数输出到缓冲区*/
        if (i == Integer.MIN_VALUE) {
            write("-2147483648");
            return;
        }

        /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */
        int size = (i < 0) ? IOUtils.stringSize(-i) + 1 : IOUtils.stringSize(i);

        int newcount = count + size;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
                expandCapacity(newcount);
            } else {
                char[] chars = new char[size];
                /** 将整数i转换成单字符并存储到chars数组 */
                IOUtils.getChars(i, size, chars);
                /** 将chars字符数组内容写到buffer中*/
                write(chars, 0, chars.length);
                return;
            }
        }

        /** 如果buffer空间够,直接将字符写到buffer中 */
        IOUtils.getChars(i, newcount, buf);
        /** 重新计数buffer中字符数 */
        count = newcount;
    }

其中值得提一下的是IOUtils.getChars,里面利用了Integer.getChars(int i, int index, char[] buf),主要的思想是整数超过65536 进行除以100, 循环取出数字后两位,依次将个位和十位转换为单字符,如果整数小于等于65536,进行除以10,取出个位数字并转换单字符,getCharts中 q = (i * 52429) >>> (16+3),可以理解为 (i乘以0.1), 但是精度更高。


2 序列化长整形数字

    public void writeLong(long i) {
        boolean needQuotationMark = isEnabled(SerializerFeature.BrowserCompatible) //
                                    && (!isEnabled(SerializerFeature.WriteClassName)) //
                                    && (i > 9007199254740991L || i < -9007199254740991L);

        if (i == Long.MIN_VALUE) {
            if (needQuotationMark) write("\"-9223372036854775808\"");
            /** 如果是长整数最小值,调用字符串函数输出到缓冲区*/
            else write("-9223372036854775808");
            return;
        }

        /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */
        int size = (i < 0) ? IOUtils.stringSize(-i) + 1 : IOUtils.stringSize(i);

        int newcount = count + size;
        if (needQuotationMark) newcount += 2;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
                expandCapacity(newcount);
            } else {
                char[] chars = new char[size];
                /** 将长整数i转换成单字符并存储到chars数组 */
                IOUtils.getChars(i, size, chars);
                if (needQuotationMark) {
                    write('"');
                    write(chars, 0, chars.length);
                    write('"');
                } else {
                    write(chars, 0, chars.length);
                }
                return;
            }
        }

        /** 添加引号 */
        if (needQuotationMark) {
            buf[count] = '"';
            IOUtils.getChars(i, newcount - 1, buf);
            buf[newcount - 1] = '"';
        } else {
            IOUtils.getChars(i, newcount, buf);
        }

        count = newcount;
    }

序列化长整型和整型非常类似,增加了双引号判断,采用用了和Integer转换为单字符同样的技巧。

 

3 序列化浮点类型数字

    public void writeDouble(double doubleValue, boolean checkWriteClassName) {
        /** 如果doubleValue不合法或者是无穷数,调用writeNull */
        if (Double.isNaN(doubleValue)
                || Double.isInfinite(doubleValue)) {
            writeNull();
        } else {
            /** 将高精度double转换为字符串 */
            String doubleText = Double.toString(doubleValue);
            /** 启动WriteNullNumberAsZero特性,会将结尾.0去除 */
            if (isEnabled(SerializerFeature.WriteNullNumberAsZero) && doubleText.endsWith(".0")) {
                doubleText = doubleText.substring(0, doubleText.length() - 2);
            }

            /** 调用字符串输出方法 */
            write(doubleText);

            /** 如果开启序列化WriteClassName特性,输出Double类型 */
            if (checkWriteClassName && isEnabled(SerializerFeature.WriteClassName)) {
                write('D');
            }
        }
    }

     public void writeFloat(float value, boolean checkWriteClassName) {
        /** 如果value不合法或者是无穷数,调用writeNull */
        if (Float.isNaN(value) //
                || Float.isInfinite(value)) {
            writeNull();
        } else {
            /** 将高精度float转换为字符串 */
            String floatText= Float.toString(value);
            /** 启动WriteNullNumberAsZero特性,会将结尾.0去除 */
            if (isEnabled(SerializerFeature.WriteNullNumberAsZero) && floatText.endsWith(".0")) {
                floatText = floatText.substring(0, floatText.length() - 2);
            }
            write(floatText);

            /** 如果开启序列化WriteClassName特性,输出float类型 */
            if (checkWriteClassName && isEnabled(SerializerFeature.WriteClassName)) {
                write('F');
            }
        }
    }

序列化浮点类型的基本思路是先转换为字符串,然后在输出到输出流中。

 

4 序列化枚举类型

    public void writeEnum(Enum<?> value) {
        if (value == null) {
            /** 如果枚举value为空,调用writeNull输出 */
            writeNull();
            return;
        }

        String strVal = null;
        /** 如果开启序列化输出枚举名字作为属性值 */
        if (writeEnumUsingName && !writeEnumUsingToString) {
            strVal = value.name();
        } else if (writeEnumUsingToString) {
            /** 采用枚举默认toString方法作为属性值 */
            strVal = value.toString();;
        }

        if (strVal != null) {
            /** 如果开启引号特性,输出json包含引号的字符串 */
            char quote = isEnabled(SerializerFeature.UseSingleQuotes) ? '\'' : '"';
            write(quote);
            write(strVal);
            write(quote);
        } else {
            /** 输出枚举所在的索引号 */
            writeInt(value.ordinal());
        }
    }

5 序列化单字符

    public void write(int c) {
        int newcount = count + 1;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                expandCapacity(newcount);
            } else {
                /** 强制流输出并刷新缓冲区 */
                flush();
                newcount = 1;
            }
        }
        /** 存储单字符到buffer并更新计数 */
        buf[count] = (char) c;
        count = newcount;
    }

6 序列化Null

    public void writeNull() {
        /** 调用输出字符串null */
        write("null");
    }

7 序列化Boolean

    public void write(boolean value) {
        if (value) {
            /** 输出true字符串 */
            write("true");
        } else {
            /** 输出false字符串 */
            write("false");
        }
    }

8 序列化16进制字节数组

    public void writeHex(byte[] bytes) {
        /** 计算总共字符长度, 乘以2 代表一个字符要占用2字节, 3代表要添加 x 和 前后添加' */
        int newcount = count + bytes.length * 2 + 3;
        if (newcount > buf.length) {
            if (writer != null) {
                char[] chars = new char[bytes.length + 3];
                int pos = 0;
                chars[pos++] = 'x';
                chars[pos++] = '\'';

                for (int i = 0; i < bytes.length; ++i) {
                    byte b = bytes[i];

                    int a = b & 0xFF;
                    /** 取字节的高四位 1111 0000*/
                    int b0 = a >> 4;
                    /** 取字节的低四位 0000 1111*/
                    int b1 = a & 0xf;

                    /** 索引低索引存储字节高位
                     *  如果4位表示的数字是 0~9, 转换为ascii的 0~9
                     *  如果4位表示的不是数字, 转换为16进制ascii码字符
                     */
                    chars[pos++] = (char) (b0 + (b0 < 10 ? 48 : 55));
                    chars[pos++] = (char) (b1 + (b1 < 10 ? 48 : 55));
                }
                chars[pos++] = '\'';
                try {
                    writer.write(chars);
                } catch (IOException ex) {
                    throw new JSONException("writeBytes error.", ex);
                }
                return;
            }
            /** buffer容量不够并且输出器为空,触发扩容 */
            expandCapacity(newcount);
        }

        buf[count++] = 'x';
        buf[count++] = '\'';

        for (int i = 0; i < bytes.length; ++i) {
            byte b = bytes[i];

            int a = b & 0xFF;
            /** 取字节的高四位 */
            int b0 = a >> 4;
            /** 取字节的低四位 */
            int b1 = a & 0xf;

            /** 索引低索引存储字节高位
             *  如果4位表示的数字是 0~9, 转换为ascii的 0~9
             *  如果4位表示的不是数字, 转换为16进制ascii码字符
             */
            buf[count++] = (char) (b0 + (b0 < 10 ? 48 : 55));
            buf[count++] = (char) (b1 + (b1 < 10 ? 48 : 55));
        }
        buf[count++] = '\'';
    }

writeHex 这个序列化方法主要对16进制的自己转换为占用2个ascii码字符,添加单引号和x前缀。

9 序列化byte字节数组

   public void writeByteArray(byte[] bytes) {
        if (isEnabled(SerializerFeature.WriteClassName.mask)) {
            /** 如果开启序列化特性WriteClassName,直接写16进制字符 */
            writeHex(bytes);
            return;
        }

        int bytesLen = bytes.length;
        final char quote = useSingleQuotes ? '\'' : '"';
        if (bytesLen == 0) {
            String emptyString = useSingleQuotes ? "''" : "\"\"";
            /** 如果字节数组长度为0,输出空白字符 */
            write(emptyString);
            return;
        }

        final char[] CA = IOUtils.CA;

        /** 验证长度是24bit位整数倍 */
        int eLen = (bytesLen / 3) * 3;
        /** base64 编码字符长度
         *
         *  base64 :
         *  第一步,将每三个字节作为一组,一共是24个二进制位。
         *  第二步,将这24个二进制位分为四组,每个组有6个二进制位。
         *  第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节。
         *  第四步,根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。
         *
         *  ref: http://www.ruanyifeng.com/blog/2008/06/base64.html
         */
        int charsLen = ((bytesLen - 1) / 3 + 1) << 2;
        // char[] chars = new char[charsLen];
        int offset = count;
        int newcount = count + charsLen + 2;
        if (newcount > buf.length) {
            if (writer != null) {
                write(quote);

                for (int s = 0; s < eLen;) {
                    /** 三个字节为一组, 扩展为四个字节 */
                    int i = (bytes[s++] & 0xff) << 16 | (bytes[s++] & 0xff) << 8 | (bytes[s++] & 0xff);

                    write(CA[(i >>> 18) & 0x3f]);
                    write(CA[(i >>> 12) & 0x3f]);
                    write(CA[(i >>> 6) & 0x3f]);
                    /** 填充00 */
                    write(CA[i & 0x3f]);
                }

                /** 对齐并编码剩余不足3个字节为一组的数据 */
                // Pad and encode last bits if source isn't even 24 bits.
                int left = bytesLen - eLen; // 0 - 2.
                if (left > 0) {

                    /**
                     * a) 1个字节的情况:
                     * 将这1字节8位二进制,每6位分成2组,最后一组除了前面加00,后面加上0000,
                     * 这样得到 两位的Base64编码, 在末尾补上2个"="号
                     *
                     * b) 2个字节的情况:
                     * 将这2字节的一共16个二进制位,每6位分成3组,最后一组除了前面加00,后面也要加00,
                     * 这样得到 3位的Base64编码, 在末尾补上"="号
                     *
                     *
                     * 如果只有1个字节,按照前面规则a)
                     * 第1组是6位,第2组后面4个0, 因此应该左移 10 = 6 + 4
                     *
                     * 如果只有2个字节,按照前面规则b)
                     * 第1个字节左移 10 位 加上 第2个字节左移 2 位补0即可
                     */
                    int i = ((bytes[eLen] & 0xff) << 10) | (left == 2 ? ((bytes[bytesLen - 1] & 0xff) << 2) : 0);

                    /** 扩展为四个字节 */
                    write(CA[i >> 12]);
                    write(CA[(i >>> 6) & 0x3f]);
                    write(left == 2 ? CA[i & 0x3f] : '=');
                    write('=');
                }

                write(quote);
                return;
            }
            expandCapacity(newcount);
        }
        count = newcount;
        buf[offset++] = quote;

        // Encode even 24-bits
        for (int s = 0, d = offset; s < eLen;) {
            /** 三个字节为一组, 扩展为四个字节 */
            int i = (bytes[s++] & 0xff) << 16 | (bytes[s++] & 0xff) << 8 | (bytes[s++] & 0xff);

            // Encode the int into four chars
            buf[d++] = CA[(i >>> 18) & 0x3f];
            buf[d++] = CA[(i >>> 12) & 0x3f];
            buf[d++] = CA[(i >>> 6) & 0x3f];
            /** 填充00 */
            buf[d++] = CA[i & 0x3f];
        }

        /** 对齐并编码剩余不足3个字节为一组的数据 */
        int left = bytesLen - eLen; // 0 - 2.
        if (left > 0) {
            // Prepare the int
            int i = ((bytes[eLen] & 0xff) << 10) | (left == 2 ? ((bytes[bytesLen - 1] & 0xff) << 2) : 0);

            /** 扩展为四个字节 */
            buf[newcount - 5] = CA[i >> 12];
            buf[newcount - 4] = CA[(i >>> 6) & 0x3f];
            buf[newcount - 3] = left == 2 ? CA[i & 0x3f] : '=';
            buf[newcount - 2] = '=';
        }
        buf[newcount - 1] = quote;
    }

writeByteArray序列化字节数组实际上就是做了base64编码转换,代码添加了详尽的注释帮助理解。

10 序列化字符串

    public void write(String str, int off, int len) {
        /** 计算总共字符串长度 */
        int newcount = count + len;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                expandCapacity(newcount);
            } else {
                /**
                 * 如果字符串str超过缓冲区大小, 进行循环拷贝
                 */
                do {
                    /** 计算当前buffer剩余容纳字符数 */
                    int rest = buf.length - count;
                    /** 将字符串str[off, off + rest) 拷贝到buf[count, ...]中*/
                    str.getChars(off, off + rest, buf, count);
                    count = buf.length;
                    /** 强制刷新输出流,会重置count = 0 */
                    flush();
                    /** 计算剩余需要拷贝的字符数量 */
                    len -= rest;
                    /** 剩余要拷贝字符在str中偏移量(索引) */
                    off += rest;
                } while (len > buf.length);
                newcount = len;
            }
        }
        /** 存储空间充足,直接将str[off, off + len) 拷贝到buf[count, ...]中*/
        str.getChars(off, off + len, buf, count);
        count = newcount;
    }

序列化字符串write(string),最终都会转化为上面形式write(string, 0, string.length)

11 序列化字符数组

    public void write(char c[], int off, int len) {
        if (off < 0 //
            || off > c.length //
            || len < 0 //
            || off + len > c.length //
            || off + len < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }

        /** 计算总共字符串长度 */
        int newcount = count + len;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                expandCapacity(newcount);
            } else {
                /**
                 * 如果字符数组c超过缓冲区大小, 进行循环拷贝
                 */
                do {
                    /** 计算当前buffer剩余容纳字符数 */
                    int rest = buf.length - count;
                    /** c[off, off + rest) 拷贝到buf[count, ...]中*/
                    System.arraycopy(c, off, buf, count, rest);
                    count = buf.length;
                    /** 强制刷新输出流,会重置count = 0 */
                    flush();
                    /** 计算剩余需要拷贝的字符数量 */
                    len -= rest;
                    /** 剩余要拷贝字符在c中偏移量(索引) */
                    off += rest;
                } while (len > buf.length);
                newcount = len;
            }
        }
        System.arraycopy(c, off, buf, count, len);
        count = newcount;
    }

12 序列化列表字符串

    public void write(char c[], int off, int len) {
        if (off < 0 //
            || off > c.length //
            || len < 0 //
            || off + len > c.length //
            || off + len < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }

        /** 计算总共字符串长度 */
        int newcount = count + len;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                expandCapacity(newcount);
            } else {
                /**
                 * 如果字符数组c超过缓冲区大小, 进行循环拷贝
                 */
                do {
                    /** 计算当前buffer剩余容纳字符数 */
                    int rest = buf.length - count;
                    /** c[off, off + rest) 拷贝到buf[count, ...]中*/
                    System.arraycopy(c, off, buf, count, rest);
                    count = buf.length;
                    /** 强制刷新输出流,会重置count = 0 */
                    flush();
                    /** 计算剩余需要拷贝的字符数量 */
                    len -= rest;
                    /** 剩余要拷贝字符在c中偏移量(索引) */
                    off += rest;
                } while (len > buf.length);
                newcount = len;
            }
        }
        System.arraycopy(c, off, buf, count, len);
        count = newcount;
    }

序列化字符串会转化成[“element”, "element", ...]格式。如果列表字符串中包含特殊字符,调用特化版本writeStringWithDoubleQuote(text, (char) 0)

13 序列化包含特殊字符字符串

    public void write(char c[], int off, int len) {
        if (off < 0 //
            || off > c.length //
            || len < 0 //
            || off + len > c.length //
            || off + len < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }

        /** 计算总共字符串长度 */
        int newcount = count + len;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                expandCapacity(newcount);
            } else {
                /**
                 * 如果字符数组c超过缓冲区大小, 进行循环拷贝
                 */
                do {
                    /** 计算当前buffer剩余容纳字符数 */
                    int rest = buf.length - count;
                    /** c[off, off + rest) 拷贝到buf[count, ...]中*/
                    System.arraycopy(c, off, buf, count, rest);
                    count = buf.length;
                    /** 强制刷新输出流,会重置count = 0 */
                    flush();
                    /** 计算剩余需要拷贝的字符数量 */
                    len -= rest;
                    /** 剩余要拷贝字符在c中偏移量(索引) */
                    off += rest;
                } while (len > buf.length);
                newcount = len;
            }
        }
        System.arraycopy(c, off, buf, count, len);
        count = newcount;
    }

writeStringWithDoubleQuote方法实现实在是太长了,这个方法主要做了以下几件事情:

1  如果开启序列化BrowserCompatible特性,执行ascii转换成native编码,节省空间。
2   如果输出器writer不为空,会自动触发buffer扩容(原有容量1.5倍+1)。
另外一个针对特殊字符的字符串序列化方法writeStringWithDoubleQuote(char[] text, final char seperator),因为和writeStringWithDoubleQuote(String text, final char seperator)版本极其类似,所以不再冗余分析。

序列化字符串的方法包括添加单引号的版本,详细请参考 writeStringWithSingleQuote :

    public void write(char c[], int off, int len) {
        if (off < 0 //
            || off > c.length //
            || len < 0 //
            || off + len > c.length //
            || off + len < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }

        /** 计算总共字符串长度 */
        int newcount = count + len;
        /** 如果当前存储空间不够 */
        if (newcount > buf.length) {
            if (writer == null) {
                expandCapacity(newcount);
            } else {
                /**
                 * 如果字符数组c超过缓冲区大小, 进行循环拷贝
                 */
                do {
                    /** 计算当前buffer剩余容纳字符数 */
                    int rest = buf.length - count;
                    /** c[off, off + rest) 拷贝到buf[count, ...]中*/
                    System.arraycopy(c, off, buf, count, rest);
                    count = buf.length;
                    /** 强制刷新输出流,会重置count = 0 */
                    flush();
                    /** 计算剩余需要拷贝的字符数量 */
                    len -= rest;
                    /** 剩余要拷贝字符在c中偏移量(索引) */
                    off += rest;
                } while (len > buf.length);
                newcount = len;
            }
        }
        System.arraycopy(c, off, buf, count, len);
        count = newcount;
    }

writeStringWithSingleQuote这个方法主要做了以下几件事情:

1  针对特殊字符,添加转译字符并且替换特殊字符为普通字符
2   如果输出器writer不为空,会自动触发buffer扩容(原有容量1.5倍+1)。

  另外一个针对特殊字符的字符串序列化方法writeStringWithSingleQuote(char[]),因为和writeStringWithSingleQuote(String)版本极其类似,所以不再冗余分析。

14 序列化字段名称

    public void writeFieldName(String key, boolean checkSpecial) {
        if (key == null) {
            /** 如果字段key为null, 输出 "null:" */
            write("null:");
            return;
        }

        if (useSingleQuotes) {
            if (quoteFieldNames) {
                /** 使用单引号并且在字段后面加':'输出 标准的json key*/
                writeStringWithSingleQuote(key);
                write(':');
            } else {
                /** 输出key,如果有特殊字符会自动添加单引号 */
                writeKeyWithSingleQuoteIfHasSpecial(key);
            }
        } else {
            if (quoteFieldNames) {
                /** 使用双引号输出json key 并添加 : */
                writeStringWithDoubleQuote(key, ':');
            } else {
                boolean hashSpecial = key.length() == 0;
                for (int i = 0; i < key.length(); ++i) {
                    char ch = key.charAt(i);
                    boolean special = (ch < 64 && (sepcialBits & (1L << ch)) != 0) || ch == '\\';
                    if (special) {
                        hashSpecial = true;
                        break;
                    }
                }
                if (hashSpecial) {
                    /** 如果包含特殊字符,会进行特殊字符转换输出,eg: 使用转换后的native编码输出 */
                    writeStringWithDoubleQuote(key, ':');
                } else {
                    /** 输出字段不加引号 */
                    write(key);
                    write(':');
                }
            }
        }
    }

序列化字段名称方法writeFieldName主要的任务:

1  完成字段特殊字符的转译
2   添加字段的引号


处理输出key的特殊字符方法writeStringWithDoubleQuote前面已经分析过了,序列化字段名称是否需要添加引号和特殊字符处理参考writeKeyWithSingleQuoteIfHasSpecial:

    private void writeKeyWithSingleQuoteIfHasSpecial(String text) {
        final byte[] specicalFlags_singleQuotes = IOUtils.specicalFlags_singleQuotes;

        int len = text.length();
        int newcount = count + len + 1;
        if (newcount > buf.length) {
            if (writer != null) {
                if (len == 0) {
                    /** 如果字段为null, 输出空白字符('':)作为key */
                    write('\'');
                    write('\'');
                    write(':');
                    return;
                }

                boolean hasSpecial = false;
                for (int i = 0; i < len; ++i) {
                    char ch = text.charAt(i);
                    if (ch < specicalFlags_singleQuotes.length && specicalFlags_singleQuotes[ch] != 0) {
                        hasSpecial = true;
                        break;
                    }
                }

                /** 如果有特殊字符,给字段key添加单引号 */
                if (hasSpecial) {
                    write('\'');
                }
                for (int i = 0; i < len; ++i) {
                    char ch = text.charAt(i);
                    if (ch < specicalFlags_singleQuotes.length && specicalFlags_singleQuotes[ch] != 0) {
                        /** 如果输出key中包含特殊字符,添加转译字符并将特殊字符替换成普通字符 */
                        write('\\');
                        write(replaceChars[(int) ch]);
                    } else {
                        write(ch);
                    }
                }

                /** 如果有特殊字符,给字段key添加单引号 */
                if (hasSpecial) {
                    write('\'');
                }
                write(':');
                return;
            }
            /** 输出器writer为null触发扩容,扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
            expandCapacity(newcount);
        }

        if (len == 0) {
            int newCount = count + 3;
            if (newCount > buf.length) {
                expandCapacity(count + 3);
            }
            buf[count++] = '\'';
            buf[count++] = '\'';
            buf[count++] = ':';
            return;
        }

        int start = count;
        int end = start + len;

        /** buffer能够容纳字符串,直接拷贝text到buf缓冲数组 */
        text.getChars(0, len, buf, start);
        count = newcount;

        boolean hasSpecial = false;

        for (int i = start; i < end; ++i) {
            char ch = buf[i];
            if (ch < specicalFlags_singleQuotes.length && specicalFlags_singleQuotes[ch] != 0) {
                if (!hasSpecial) {
                    newcount += 3;
                    if (newcount > buf.length) {
                        expandCapacity(newcount);
                    }
                    count = newcount;

                    /** 将字符后移两位,插入字符'\ 并替换特殊字符为普通字符 */
                    System.arraycopy(buf, i + 1, buf, i + 3, end - i - 1);
                    /** 将字符后移一位 */
                    System.arraycopy(buf, 0, buf, 1, i);
                    buf[start] = '\'';
                    buf[++i] = '\\';
                    buf[++i] = replaceChars[(int) ch];
                    end += 2;
                    buf[count - 2] = '\'';

                    hasSpecial = true;
                } else {
                    newcount++;
                    if (newcount > buf.length) {
                        expandCapacity(newcount);
                    }
                    count = newcount;

                    /** 包含特殊字符,将字符后移一位,插入转译字符\ 并替换特殊字符为普通字符 */
                    System.arraycopy(buf, i + 1, buf, i + 2, end - i);
                    buf[i] = '\\';
                    buf[++i] = replaceChars[(int) ch];
                    end++;
                }
            }
        }

        buf[newcount - 1] = ':';
    }

15 序列化Boolean类型字段键值对

    public void writeFieldValue(char seperator, String name, boolean value) {
        if (!quoteFieldNames) {
            /** 如果不需要输出双引号,则一次输出字段分隔符,字段名字,字段值 */
            write(seperator);
            writeFieldName(name);
            write(value);
            return;
        }
        /** true 占用4位, false 占用5位 */
        int intSize = value ? 4 : 5;

        int nameLen = name.length();
        /** 输出总长度, 中间的4  代表 key 和 value 总共占用4个引号 */
        int newcount = count + nameLen + 4 + intSize;
        if (newcount > buf.length) {
            if (writer != null) {
                /** 依次输出字段分隔符,字段:字段值 */
                write(seperator);
                writeString(name);
                write(':');
                write(value);
                return;
            }
            /** 输出器writer为null触发扩容,扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
            expandCapacity(newcount);
        }

        int start = count;
        count = newcount;

        /** 输出字段分隔符,一般是, */
        buf[start] = seperator;

        int nameEnd = start + nameLen + 1;

        /** 输出字段属性分隔符,一般是单引号或双引号 */
        buf[start + 1] = keySeperator;

        /** 输出字段名称 */
        name.getChars(0, nameLen, buf, start + 2);

        /** 字段名称添加分隔符,一般是单引号或双引号 */
        buf[nameEnd + 1] = keySeperator;

        /** 输出boolean类型字符串值 */
        if (value) {
            System.arraycopy(":true".toCharArray(), 0, buf, nameEnd + 2, 5);
        } else {
            System.arraycopy(":false".toCharArray(), 0, buf, nameEnd + 2, 6);
        }
    }

序列化boolean类型的键值对属性,因为不涉及特殊字符,主要就是把原型序列化为字面量值。

16 序列化Int类型字段键值对

    public void writeFieldValue(char seperator, String name, int value) {
        if (value == Integer.MIN_VALUE || !quoteFieldNames) {
            /** 如果是整数最小值或不需要输出双引号,则一次输出字段分隔符,字段名字,字段值 */
            write(seperator);
            writeFieldName(name);
            writeInt(value);
            return;
        }

        /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */
        int intSize = (value < 0) ? IOUtils.stringSize(-value) + 1 : IOUtils.stringSize(value);

        int nameLen = name.length();
        int newcount = count + nameLen + 4 + intSize;
        if (newcount > buf.length) {
            if (writer != null) {
                write(seperator);
                writeFieldName(name);
                writeInt(value);
                return;
            }
            /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
            expandCapacity(newcount);
        }

        int start = count;
        count = newcount;

        /** 输出字段分隔符,一般是, */
        buf[start] = seperator;

        int nameEnd = start + nameLen + 1;

        /** 输出字段属性分隔符,一般是单引号或双引号 */
        buf[start + 1] = keySeperator;

        /** 输出字段名称 */
        name.getChars(0, nameLen, buf, start + 2);

        buf[nameEnd + 1] = keySeperator;
        buf[nameEnd + 2] = ':';

        /** 输出整数值,对整数转化成单字符 */
        IOUtils.getChars(value, count, buf);
    }

序列化int类型的键值对属性,因为不涉及特殊字符,主要就是把原型序列化为字面量值。截止到现在,已经把核心SerializWriter类讲完了,剩余字段键值对极其类似writeFieldValue boolean和int等,因此无需冗余分析。因为序列化真正开始之前,这个类极其基础并且非常重要,因此花的时间较多。
 

二 JSON成员函数

概要

fastjson序列化主要使用入口就是在JSON.java类中,它提供非常简便和友好的api将java对象转换成json字符串。

JSON成员函数

    /**
     *  便捷序列化java对象,序列化对象可以包含任意泛型属性字段,但是不适用本身是泛型的对象。
     *  默认序列化返回字符串,可以使用writeJSONString(Writer, Object, SerializerFeature[])
     *  将序列化字符串输出到指定输出器中
     */
    public static String toJSONString(Object object) {
        /**
         * 直接调用重载方法,将指定object序列化成json字符串,忽略序列化filter
         */
        return toJSONString(object, emptyFilters);
    }

使用便捷接口toJSONString方法,可以将任意java对象序列化为json字符串,内部调用toJSONString(Object, SerializeFilter[], SerializerFeature... ) :

    public static String toJSONString(Object object, SerializeFilter[] filters, SerializerFeature... features) {
        return toJSONString(object, SerializeConfig.globalInstance, filters, null, DEFAULT_GENERATE_FEATURE, features);
    }

继续跟踪方法调用到toJSONString(Object, SerializeConfig ,SerializeFilter[], String, int, SerializerFeature... ) :

    public static String toJSONString(Object object,                   /** 序列化对象    */
                                      SerializeConfig config,          /** 全局序列化配置 */
                                      SerializeFilter[] filters,       /** 序列化拦截器   */
                                      String dateFormat,               /** 序列化日期格式 */
                                      int defaultFeatures,             /** 默认序列化特性 */
                                      SerializerFeature... features) { /** 自定义序列化特性 */
        /** 初始化序列化writer,用features覆盖defaultFeatures配置 */
        SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);

        try {

            /**
             *  初始化JSONSerializer,序列化类型由它委托config查找具体
             *  序列化处理器处理,序列化结果写入out的buffer中
             */
            JSONSerializer serializer = new JSONSerializer(out, config);

            if (dateFormat != null && dateFormat.length() != 0) {
                serializer.setDateFormat(dateFormat);
                /** 调用out 重新配置属性 并且打开WriteDateUseDateFormat特性 */
                serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
            }

            if (filters != null) {
                for (SerializeFilter filter : filters) {
                    /** 添加拦截器 */
                    serializer.addFilter(filter);
                }
            }

            /** 使用序列化实例转换对象,查找具体序列化实例委托给config查找 */
            serializer.write(object);

            return out.toString();
        } finally {
            out.close();
        }
    }

这个序列化方法实际并不是真正执行序列化操作,首先做序列化特性配置,然后追加序列化拦截器,开始执行序列化对象操作委托给了config对象查找。

我们继续进入serializer.write(object) 查看:

    public final void write(Object object) {
        if (object == null) {
            /** 如果对象为空,直接输出 "null" 字符串 */
            out.writeNull();
            return;
        }

        Class<?> clazz = object.getClass();
        /** 根据对象的Class类型查找具体序列化实例 */
        ObjectSerializer writer = getObjectWriter(clazz);

        try {
            /** 使用具体serializer实例处理对象 */
            writer.write(this, object, null, null, 0);
        } catch (IOException e) {
            throw new JSONException(e.getMessage(), e);
        }
    }

 

序列化回调接口

ObjectSerializer序列化接口

我们发现真正序列化对象的时候是由具体ObjectSerializer实例完成,我们首先查看一下接口定义:

    void write(JSONSerializer serializer, /** json序列化实例 */
               Object object,       /** 待序列化的对象*/
               Object fieldName,    /** 待序列化字段*/
               Type fieldType,      /** 待序列化字段类型 */
               int features) throws IOException;

当fastjson序列化特定的字段时会回调这个方法。

我们继续跟踪writer.write(this, object, null, null, 0) :

    public final void write(Object object) {
        if (object == null) {
            /** 如果对象为空,直接输出 "null" 字符串 */
            out.writeNull();
            return;
        }

        Class<?> clazz = object.getClass();
        /** 根据对象的Class类型查找具体序列化实例 */
        ObjectSerializer writer = getObjectWriter(clazz);

        try {
            /** 使用具体serializer实例处理对象 */
            writer.write(this, object, null, null, 0);
        } catch (IOException e) {
            throw new JSONException(e.getMessage(), e);
        }
    }

我们发现在方法内部调用getObjectWriter(clazz)根据具体类型查找序列化实例,方法内部只有一行调用 config.getObjectWriter(clazz),让我们更进一步查看委托实现细节com.alibaba.fastjson.serializer.SerializeConfig#getObjectWriter(java.lang.Class<?>):
 

    public ObjectSerializer getObjectWriter(Class<?> clazz) {
        return getObjectWriter(clazz, true);
    }

内部又调用com.alibaba.fastjson.serializer.SerializeConfig#getObjectWriter(java.lang.Class<?>, boolean),这个类实现相对复杂了一些,我会按照代码顺序梳理所有序列化实例的要点 :

    private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
        /** 首先从内部已经注册查找特定class的序列化实例 */
        ObjectSerializer writer = serializers.get(clazz);

        if (writer == null) {
            try {
                final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                /** 使用当前线程类加载器 查找 META-INF/services/AutowiredObjectSerializer.class实现类 */
                for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
                    if (!(o instanceof AutowiredObjectSerializer)) {
                        continue;
                    }

                    AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
                    for (Type forType : autowired.getAutowiredFor()) {
                        /** 如果存在,注册到内部serializers缓存中 */
                        put(forType, autowired);
                    }
                }
            } catch (ClassCastException ex) {
                // skip
            }

            /** 尝试在已注册缓存找到特定class的序列化实例 */
            writer = serializers.get(clazz);
        }

        if (writer == null) {
            /** 使用加载JSON类的加载器 查找 META-INF/services/AutowiredObjectSerializer.class实现类 */
            final ClassLoader classLoader = JSON.class.getClassLoader();
            if (classLoader != Thread.currentThread().getContextClassLoader()) {
                try {
                    for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {

                        if (!(o instanceof AutowiredObjectSerializer)) {
                            continue;
                        }

                        AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
                        for (Type forType : autowired.getAutowiredFor()) {
                            /** 如果存在,注册到内部serializers缓存中 */
                            put(forType, autowired);
                        }
                    }
                } catch (ClassCastException ex) {
                    // skip
                }

                /** 尝试在已注册缓存找到特定class的序列化实例 */
                writer = serializers.get(clazz);
            }
        }

        if (writer == null) {
            String className = clazz.getName();
            Class<?> superClass;

            if (Map.class.isAssignableFrom(clazz)) {
                /** 如果class实现类Map接口,使用MapSerializer序列化 */
                put(clazz, writer = MapSerializer.instance);
            } else if (List.class.isAssignableFrom(clazz)) {
                /** 如果class实现类List接口,使用ListSerializer序列化 */
                put(clazz, writer = ListSerializer.instance);
            } else if (Collection.class.isAssignableFrom(clazz)) {
                /** 如果class实现类Co
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haikuotiankongdong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值