日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【网上的都不靠谱?还是得改源码】用Javasisst的字节码插桩技术,彻底解决Gson转Map时,Int变成double问题...

發布時間:2025/4/5 java 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【网上的都不靠谱?还是得改源码】用Javasisst的字节码插桩技术,彻底解决Gson转Map时,Int变成double问题... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、探究原由

首先申明一下,我們要解決的問題有兩個:

  • Json串轉Map時,int變double問題
  • Json串轉對象時,對象屬性中的Map,int變double問題

然后,我們來了解一下,Gson實現Json反序列化的源碼:

  • Gson內部會維護一個類型適配器集合,里面大概有十多個內置的TypeAdapter。涵蓋了八大基本類型的TypeAdapter,并且還有一個ObjectTypeAdapter。同時Gson支持自定義TypeAdapter,可以在內置的適配器集合中添加新的類型適配器
  • 在具體的Json數據反序列化時,首先會根據傳入的對象Class,來獲取對應的TypeAdapter,然后根據獲取的TypeAdapter實現Json到對象的轉換。
  • 因此,在反序列化時,int(Integer)、string等對象屬性能匹配到對應的TypeAdapter,進行正確的反序列化。但是如果對象屬性為Map時(或者本身就是Json串轉Map),將默認由ObjectTypeAdapter類來完成數據的解析。
  • ObjectTypeAdapter的核心代碼:
  • @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對象,這也就是問題的根源。

    二、網上的“半”解決方案

    網羅了網上的解決方案,無非就以下幾種。

    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>>() {@Overridepublic 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) {@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(); 復制代碼

    三、徹底的解決方案

    我們知道,還有一種徹底的解決方案,那就是修改源代碼。但是修改源代碼是一件痛苦的事情:

    • 需要解決各種依賴環境問題
    • 有些沒有源碼包的還需要反編譯成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问题...的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。