首先申明一下,咱們要解決的問題有兩個:java
而後,咱們來了解一下,Gson實現Json反序列化的源碼:json
類型適配器
集合,裏面大概有十多個內置的TypeAdapter。涵蓋了八大基本類型的TypeAdapter,而且還有一個ObjectTypeAdapter。同時Gson支持自定義TypeAdapter,能夠在內置的適配器集合中添加新的類型適配器
@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:
return in.nextDouble();
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
複製代碼
上面能夠看到,針對全部的Number類型,均使用了nextDouble()
來返回了一個Double對象,這也就是問題的根源。ide
網羅了網上的解決方案,無非就如下幾種。性能
從新添加一個自定義的TypeAdapter,解決實現Json串轉Map。注意:它解決了Json串轉Map問題,可是未能解決Json串轉對象問題
!測試
Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<TreeMap<String, Object>>(){}.getType(),
new JsonDeserializer<TreeMap<String, Object>>() {
@Override
public TreeMap<String, Object> deserialize( JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
TreeMap<String, Object> treeMap = new TreeMap<>();
JsonObject jsonObject = json.getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
for (Map.Entry<String, JsonElement> entry : entrySet) {
treeMap.put(entry.getKey(), entry.getValue());
}
return treeMap;
}
}).create();
複製代碼
從新添加一個自定義的TypeAdapter,解決實現Json串轉指定對象。注意:它僅僅解決了Json串轉指定對象問題,可是未能解決Json串轉Map問題
!ui
而且經測試,如下代碼使用時會報錯,緣由不明……this
public final class MyTypeAdapter extends TypeAdapter<Object> {
public static final FACTORY(Class clazz) {
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (type.getRawType() == clazz) {
return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
}
return null;
}
};
private final Gson gson;
ObjectTypeAdapter(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:
Double tmp = in.nextDouble();
if (tmp.longValue() = tmp.doubleValue)
return Long.valueOf(tmp.longValue());
return tmp;
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);
}
}
//使用
Gson gson = new GsonBuilder().registerTypeAdapterFactory(MyTypeAdaptor.FACTORY(Person.class)).create();
複製代碼
咱們知道,還有一種完全的解決方案,那就是修改源代碼。可是修改源代碼是一件痛苦的事情:google
所以,咱們嘗試用Javasisst進行字節碼插樁!spa
簡單入門使用,看這篇簡書就好:www.jianshu.com/p/b9b3ff0e1…code
簡單概括就是,讀取原class文件,修改類、方法、屬性等,而後從新生成class字節碼文件
咱們使用一個叫作insertAt()
的方法,按行號來插入代碼段(若是行號表包含在類文件中),將編譯後的代碼插入到指定行號位置。
注意:行號是源文件jar包中相關位置的行號。
下載好gson-2.7.jar
、gson-2.7-sources.jar
這兩個文件。 而後從gson-2.7-sources.jar
中找到要修改的相關類的具體行號位置:
com.google.gson.internal.bind.ObjectTypeAdapter
而後書寫插樁代碼:
/** * @Description: javasisst插樁 * @Author localhost01.cn * @Date: Created in 22:29 2019-03-27 */
public class Main {
public static void main(String[] args) throws Exception {
// 1.獲得反編譯的池
ClassPool pool = ClassPool.getDefault();
// 2.導入須要用到的包
pool.importPackage("com.google.gson.stream");
pool.importPackage("java.io");
pool.importPackage("java.util");
pool.importPackage("java.lang");
pool.importPackage("com.google.gson.internal");
// 3.取得須要反編譯的jar文件
pool.insertClassPath("D:\\gson-2.7.jar");
// 4.取得須要反編譯要修改的類,注意是全路徑
CtClass cc = pool.get("com.google.gson.internal.bind.ObjectTypeAdapter");
// 5.取得須要修改的方法
CtMethod method = cc.getDeclaredMethod("read");
method.insertAt(78, "if (true){\n"
+ " Double tmp = Double.valueOf(in.nextDouble());\n"
+ " if (tmp.longValue() == tmp.doubleValue()) {\n"
+ " return Long.valueOf( tmp.longValue());\n"
+ " } else {\n"
+ " return tmp;\n"
+ " }\n"
+ "}");
// 6.寫入
cc.writeFile(); //這兒也能夠傳入一個參數,指定新class要輸出的位置
System.out.println("alright!");
}
}
複製代碼
OK,把生成的ObjectTypeAdapter.class文件替換到gson-2.7.jar
包的相關位置便可。
到這兒就結束了!
你覺得還很複雜?