Gson与Jackson在反序列化数字类型时的差异

Java序列化与反序列化Json通常会使用GsonJackson这两个框架,之前因为公司都是用的Gson也没太在意。最近碰到了一个这两框架在反序列化上有些不太一样的地方。

通常在Java中我们反序列化一个Json字符串是,如果我们不想定义一个具体的类,我们会把它反序列化为Java中的Map类,这时候我们其实没有提供任何Map中的value的类型的信息,序列化框架需要自己根据json字符串来判断应该反序列化为哪个类。这里我发现Gson在处理数字的反序列化,会默认都反序列化成Double,而Jackson则可以智能的判断。

为什么会产生这样的差异?查阅源码以后我发现,这两个框架在对JsonToken的定义上就存在差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Jackson的JsonToken定义
*/
public enum JsonToken
{
NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),
START_OBJECT("{", JsonTokenId.ID_START_OBJECT),
END_OBJECT("}", JsonTokenId.ID_END_OBJECT),
START_ARRAY("[", JsonTokenId.ID_START_ARRAY),
END_ARRAY("]", JsonTokenId.ID_END_ARRAY),
FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),
VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT),
VALUE_STRING(null, JsonTokenId.ID_STRING),
VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),
VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),
VALUE_TRUE("true", JsonTokenId.ID_TRUE),
VALUE_FALSE("false", JsonTokenId.ID_FALSE),
VALUE_NULL("null", JsonTokenId.ID_NULL),
;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Gson的JsonToken定义
*/
public enum JsonToken {
BEGIN_ARRAY,
END_ARRAY,
BEGIN_OBJECT,
END_OBJECT,
NAME,
STRING,
NUMBER,
BOOLEAN,
NULL,
END_DOCUMENT
}

可以看到,Jackson在JsonToken中就将数字定义成两种类型VALUE_NUMBER_INTVALUE_NUMBER_FLOAT,而Gson中只有一种NUMBER

事实上这两个框架在解析数字时都区分了整型和浮点型,代码可以参见

Gson中JsonReader的peekNumber()方法

Jackson中ReaderBasedJsonParser的_parsePosNumber()方法

但是Gson在将其转成JsonToken时将PEEKED_LONGPEEKED_NUMBER统一返回成了JsonToken.NUMBER

可以参见JsonReader中的peek()函数

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
/**
* Returns the type of the next token without consuming it.
*/
public JsonToken peek() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
}
switch (p) {
case PEEKED_BEGIN_OBJECT:
return JsonToken.BEGIN_OBJECT;
case PEEKED_END_OBJECT:
return JsonToken.END_OBJECT;
case PEEKED_BEGIN_ARRAY:
return JsonToken.BEGIN_ARRAY;
case PEEKED_END_ARRAY:
return JsonToken.END_ARRAY;
case PEEKED_SINGLE_QUOTED_NAME:
case PEEKED_DOUBLE_QUOTED_NAME:
case PEEKED_UNQUOTED_NAME:
return JsonToken.NAME;
case PEEKED_TRUE:
case PEEKED_FALSE:
return JsonToken.BOOLEAN;
case PEEKED_NULL:
return JsonToken.NULL;
case PEEKED_SINGLE_QUOTED:
case PEEKED_DOUBLE_QUOTED:
case PEEKED_UNQUOTED:
case PEEKED_BUFFERED:
return JsonToken.STRING;
case PEEKED_LONG: //这里
case PEEKED_NUMBER:
return JsonToken.NUMBER;
case PEEKED_EOF:
return JsonToken.END_DOCUMENT;
default:
throw new AssertionError();
}
}

而上层在反序列化时是以JsonToken为类型的参考依据的(当用户没有提供类型信息时),比如我们可以看ObjectTypeAdapter(通常我们不给类型,gson就会按Object类型来反序列化)中的反序列化

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
public final class ObjectTypeAdapter extends TypeAdapter<Object> {
// ...省略一些无关代码
@Override public Object read(JsonReader in) throws IOException {
JsonToken token = in.peek();
switch (token) {
case BEGIN_ARRAY:
List<Object> list = new ArrayList<Object>();
in.beginArray();
while (in.hasNext()) {
list.add(read(in));
}
in.endArray();
return list;
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedTreeMap<String, Object>();
in.beginObject();
while (in.hasNext()) {
map.put(in.nextName(), read(in));
}
in.endObject();
return map;
case STRING:
return in.nextString();
case NUMBER: // 这里根据JsonToken的类型进行反序列化
return in.nextDouble();
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
// ...省略一些无关代码
}

综上所述,gson其实是可以做到识别整型和浮点型,但是在上层隐藏了这个信息,不知道是出于什么考虑。