背景:
Gson在使用gson.fromJson对json对象中带int类型的数据转换时候,会存在nt类型转换为double的情况,原因是Gson在解释数据过程中的number类型处理导致的;具体见ObjectTypeAdapter类 的read方法
@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();
// 处理number类型数据,会自动转换为double类型
case NUMBER:
return in.nextDouble();
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
代码中使用到的Gson版本为2.8.6版本,笔者本人也尝试过使用Gson的高版本2.9.1版本,Number类型的实现方式稍有出入,不过结果还是相同解析int类型数据依旧转换为double
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.4</version>
</dependency>
示例:
public static void main(String[] args) {
String str = "{\"msg\":\"操作成功\",\"code\":200,\"data\":\"$f8ff483ee9d57d5ef5d1adf4385a24b\"}";
BaseResult baseResult = new Gson().fromJson(str,BaseResult.class);
System.out.println(111);
}
解决方案:自定义TypeAdapter,复制ObjectTypeAdapter中的所有代码,只修改read方法中关于Number类型的处理
package com.yojofly.server.config;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.*;
@Component
public class GsonTypeAdaptor extends TypeAdapter<Object> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked")
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (type.getRawType() == Object.class) {
return (TypeAdapter<T>) new GsonTypeAdaptor(gson);
}
return null;
}
};
private Gson gson;
GsonTypeAdaptor(Gson gson) {
this.gson = gson;
}
@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:
// 自定义的number类型处理
String s = in.nextString();
if (s.contains(".")) {
return Double.valueOf(s);
} else {
try {
return Integer.valueOf(s);
} catch (Exception e) {
return Long.valueOf(s);
}
}
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
@SuppressWarnings("unchecked")
@Override public void write(JsonWriter out, Object value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
if (typeAdapter instanceof ObjectTypeAdapter) {
out.beginObject();
out.endObject();
return;
}
typeAdapter.write(out, value);
}
}
最后需要将自定义的GsonTypeAdaptor注册到GsonBuilder中
public static final Gson gson = getGson();
public static Gson getGson() {
Gson gson = new GsonBuilder().create();
try {
Field factories = Gson.class.getDeclaredField("factories");
factories.setAccessible(true);
Object o = factories.get(gson);
Class<?>[] declaredClasses = Collections.class.getDeclaredClasses();
for (Class c : declaredClasses) {
if ("java.util.Collections$UnmodifiableList".equals(c.getName())) {
Field listField = c.getDeclaredField("list");
listField.setAccessible(true);
List<TypeAdapterFactory> list = (List<TypeAdapterFactory>) listField.get(o);
int i = list.indexOf(ObjectTypeAdapter.FACTORY);
list.set(i, GsonTypeAdaptor.FACTORY);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return gson;
}
示例:
public static void main(String[] args) {
String str = "{\"msg\":\"操作成功\",\"code\":200,\"data\":\"$f8ff483ee9d57d5ef5d1adf4385a24b\"}";
BaseResult baseResult = gson.fromJson(str,BaseResult.class);
System.out.println(111);
}
最后解决该问题。
网上也有通过如下代码实现自定义typeAdapter注册,但笔者多次尝试如下代码并不生效
new GsonBuilder().registerTypeAdapter(new TypeToken<Map<String,Object>>(){}.getType(),new DataTypeAdapter()).create();