用gson将string反序列化为Map时,int类型变成了double类型。
原因分析
new Gson().fromJson("{\"aid\":123,\"aa\":11.0}", Map.class); // 结果为{"aid":123.0,"aa":11.0}
通过看gson解析过程,gson通过TypeAdapter的read方法来读取string的值并转化为对象:
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true);
TypeAdapter typeAdapter;
try {
try {
reader.peek();
isEmpty = false;
TypeToken<T> typeToken = TypeToken.get(typeOfT);
typeAdapter = this.getAdapter(typeToken);
T object = typeAdapter.read(reader);
Object var8 = object;
return var8;
} catch (EOFException var15) {
if (!isEmpty) {
throw new JsonSyntaxException(var15);
}
} catch (IllegalStateException var16) {
throw new JsonSyntaxException(var16);
} catch (IOException var17) {
throw new JsonSyntaxException(var17);
} catch (AssertionError var18) {
throw new AssertionError("AssertionError (GSON 2.8.5): " + var18.getMessage(), var18);
}
typeAdapter = null;
} finally {
reader.setLenient(oldLenient);
}
return typeAdapter;
}
而TypeAdapter的类型通过TypeToken获取,所有TypeAdapter存放在gson对下的factories成员变量中,在转换时指定转换类型为Map.class,key和value默认为Object类型,所以开始获取的TypeAdapter为MapTypeAdapterFactory.Adapter类型
Iterator var7 = this.factories.iterator();
TypeAdapter candidate;
do {
if (!var7.hasNext()) {
throw new IllegalArgumentException("GSON (2.8.5) cannot handle " + type);
}
TypeAdapterFactory factory = (TypeAdapterFactory)var7.next();
candidate = factory.create(this, type);
} while(candidate == null);
call.setDelegate(candidate);
this.typeTokenCache.put(type, candidate);
TypeAdapter var10 = candidate;
return var10;
其value对应的TypeAdapter为ObjectTypeAdapter,而对于NUMBER类型的数据均会识别为double类型。
public Object read(JsonReader in) throws IOException {
JsonToken token = in.peek();
switch(token) {
case BEGIN_ARRAY:
List<Object> list = new ArrayList();
in.beginArray();
while(in.hasNext()) {
list.add(this.read(in));
}
in.endArray();
return list;
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedTreeMap();
in.beginObject();
while(in.hasNext()) {
map.put(in.nextName(), this.read(in));
}
in.endObject();
return map;
case STRING:
return in.nextString();
case NUMBER:
return in.nextDouble();
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
解决方案
重新定义一个TypeAdapter期望来覆盖ObjectTypeAdapter,实现同现有的ObjectTypeAdapter,但是修改Number解析:
case NUMBER:
String numberStr = in.nextString();
//返回的numberStr不会为null
if (numberStr.contains(".") || numberStr.contains("e")
|| numberStr.contains("E")) {
return Double.parseDouble(numberStr);
}
return Long.parseLong(numberStr);
注册到gson对象中如下:
new GsonBuilder().registerTypeAdapterFactory(ObjectTypeAdapter.FACTORY).create();
运行结果还是将int转换为了double类型。原因是register后的typeAdapter被add到factory的后面,而在查找typeAdater时从前往后遍历,还是找到了之前的ObjectTypeAdapter。
重新定义一个新的类型(gson默认没有的类型)确保查找时会找到创建的adpter:
public final class ObjectTypeAdapter extends TypeAdapter<Object> {
private static final Gson gson = new Gson();
private TypeAdapter objectTypeAdapter = gson.getAdapter(Object.class);
public Object read(JsonReader in) throws IOException {
JsonToken token = in.peek();
switch(token) {
case BEGIN_ARRAY:
List<Object> list = new ArrayList();
in.beginArray();
while(in.hasNext()) {
list.add(this.read(in));
}
in.endArray();
return list;
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedTreeMap();
in.beginObject();
while(in.hasNext()) {
map.put(in.nextName(), this.read(in));
}
in.endObject();
return map;
case STRING:
return in.nextString();
case NUMBER:
String numberStr = in.nextString();
//返回的numberStr不会为null
if (numberStr.contains(".") || numberStr.contains("e")
|| numberStr.contains("E")) {
return Double.parseDouble(numberStr);
}
return Long.parseLong(numberStr);
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
public void write(JsonWriter out, Object value) throws IOException {
objectTypeAdapter.write(out, value);
}
}
注册并调用如下,结果成功。
Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<Map<String, Object>>(){}.getType(), new ObjectTypeAdapter()).serializeNulls().create();
gson.fromJson("{\"aid\":123,\"aa\":11.0}", new TypeToken<Map<String, Object>>(){}.getType());