序列化(三)

SerializeWriter成员函数

序列化字段名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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] = ':';
}

序列化Boolean类型字段键值对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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类型的键值对属性,因为不涉及特殊字符,主要就是把原型序列化为字面量值。

序列化Int类型字段键值对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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等,因此无需冗余分析。因为序列化真正开始之前,这个类极其基础并且非常重要,因此花的时间较多。

感谢您的阅读,本文由 诣极的博客 版权所有。如若转载,请注明出处:诣极的博客(https://zonghaishang.github.io/2018/09/30/Fastjson源码解析-序列化(三)-序列化字段属性键值对/
序列化(二)
序列化(四)