【网上的都不靠谱?还是得改源码】用Javasisst的字节码插桩技术,彻底解决Gson转Map时,Int变成double问题...
一、探究原由
首先申明一下,我們要解決的問題有兩個:
- Json串轉Map時,int變double問題
- Json串轉對象時,對象屬性中的Map,int變double問題
然后,我們來了解一下,Gson實現Json反序列化的源碼:
上面可以看到,針對所有的Number類型,均使用了nextDouble()來返回了一個Double對象,這也就是問題的根源。
二、網上的“半”解決方案
網羅了網上的解決方案,無非就以下幾種。
2.1 自定義一個適配TreeMap的TypeAdapter
重新添加一個自定義的TypeAdapter,解決實現Json串轉Map。注意:它解決了Json串轉Map問題,但是未能解決Json串轉對象問題!
Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<TreeMap<String, Object>>(){}.getType(), new JsonDeserializer<TreeMap<String, Object>>() {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(); 復制代碼2.2 自定義一個適配指定類的TypeAdapter
重新添加一個自定義的TypeAdapter,解決實現Json串轉指定對象。注意:它僅僅解決了Json串轉指定對象問題,但是未能解決Json串轉Map問題!
并且經測試,以下代碼使用時會報錯,原因不明……
public final class MyTypeAdapter extends TypeAdapter<Object> {public static final FACTORY(Class clazz) { 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;} 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();}}("unchecked") 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(); 復制代碼三、徹底的解決方案
我們知道,還有一種徹底的解決方案,那就是修改源代碼。但是修改源代碼是一件痛苦的事情:
- 需要解決各種依賴環境問題
- 有些沒有源碼包的還需要反編譯成Java文件
- 重新打包,重新打包有時不那么順利,可能出現各種JavaDoc問題之類的……
- 各種麻煩,誰用誰知道……
因此,我們嘗試用Javasisst進行字節碼插樁!
3.1 Javasisst入門
簡單入門使用,看這篇簡書就好:www.jianshu.com/p/b9b3ff0e1…
簡單歸納就是,讀取原class文件,修改類、方法、屬性等,然后重新生成class字節碼文件
我們使用一個叫做insertAt()的方法,按行號來插入代碼段(如果行號表包含在類文件中),將編譯后的代碼插入到指定行號位置。
注意:行號是源文件jar包中相關位置的行號。
3.2 方法步驟
下載好gson-2.7.jar、gson-2.7-sources.jar這兩個文件。 然后從gson-2.7-sources.jar中找到要修改的相關類的具體行號位置:
com.google.gson.internal.bind.ObjectTypeAdapter
注意:行號應是78,而不是79!然后書寫插樁代碼:
/*** @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包的相關位置即可。
到這兒就結束了!
你以為還很復雜?
轉載于:https://juejin.im/post/5c9d6eb2f265da61125650bb
總結
以上是生活随笔為你收集整理的【网上的都不靠谱?还是得改源码】用Javasisst的字节码插桩技术,彻底解决Gson转Map时,Int变成double问题...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: three.js学习资料整理
- 下一篇: Ubuntu16.04安装opencv-