1、现状及原因

目前网络上的资料中有两种解决这个问题的办法。

第一种:修改源码。

第二种:添加指定类型的自定义解密工具。

所有能找到的资料中,都对第一种进行了忽略,认为修改源码改动太大,转而使用了第二种方式。

不论是注册Map的解析器,还是使用自定义的Bean对象,本质都是指定类型解析,也就是第二种,完全无法解决以下问题。

gson.fromJson(s,new TypeToken<List<Map<String,Object>>>(){}.getType());

在这段代码中,你可以测试网络中找到的其他解决方式,你会发现所有设置都失效了。

原因是,当你使用 Class 对象作为解析类型时,Gson 会转入你自定义的解析器,而使用 TypeToken 时,Gson 无法将传入的参数与绑定的 TypeToken 对应,最终使用默认解析器。

2、最终解决办法

由上面的讲解过程,你会明白,第二种方法无法实现,只有修改源码才能达到最终目标。

大多数人没有意识到,修改源码不一定要重新编译,反射一样可以实现。

Gson 中默认处理数值转换的类为 com.google.gson.internal.bind.ObjectTypeAdapter,我们只需要在内存中将其替换即可。

ObjectTypeAdapter 在创建的 Gson 对象中是不存在的,其使用了内部的工厂对象(FACTORY)动态创建。

而 FACTORY 会在 Gson 的构造函数中加入 factories 对象中。

最终,factories 对象通过 Collections 的方法变为不可变列表后保存为成功变量。

找来找去,发现,我们只需要把 Gson 实例中的 factories 对象内部的工厂对象取代即可。

3、实现过程

第一步,创建自定义的 ObjectTypeAdapter 对象,并实现其工厂方法。

public final class MapTypeAdapter extends TypeAdapter<Object> {public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {@SuppressWarnings("unchecked")@Overridepublic <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {if (type.getRawType() == Object.class) {return (TypeAdapter<T>) new MapTypeAdapter(gson);}return null;}};private final Gson gson;private MapTypeAdapter(Gson gson) {this.gson = gson;}@Overridepublic 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(read(in));}in.endArray();return list;case BEGIN_OBJECT:Map<String, Object> map = new LinkedTreeMap<>();in.beginObject();while (in.hasNext()) {map.put(in.nextName(), read(in));}in.endObject();return map;case STRING:return in.nextString();case 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();}}@Overridepublic void write(JsonWriter out, Object value) throws IOException {if (value == null) {out.nullValue();return;}//noinspection uncheckedTypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());if (typeAdapter instanceof ObjectTypeAdapter) {out.beginObject();out.endObject();return;}typeAdapter.write(out, value);}
}

其实改动部分只有对 NUMBER 分支的细化,将原始数据是否包含小数点来作为其是否为整数与小数的依据。 我认为原始数据中字面值为 1.0 的数是小数,而字面值为 1 的数为整数。你也可以有自己的实现方式。

第二步,使用自定义工厂方法取代 Gson 实例中的工厂方法。

    public 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, MapTypeAdapter.FACTORY);break;}}} catch (Exception e) {e.printStackTrace();}return gson;}

代码中,首先获得 gson 实例的 factories 属性,将属性设置为 public 访问权限,然后获得其属性 o。

因为在 gson 的创建过程中,factories 通过 Collections 的方法变为了不可修改对象,所以我们需要将其真实属性获得才能进行修改。

通过 Collections 的字节码对象获得其声明的所有内部类,遍历内部类获得 UnmodifiableList 类的字节码对象,最后获得其进行包装之前的真实列表数据 listField,并设置其访问权限为 public。

最终获得了真实的 factories 列表 list。

最后一步,得到 ObjectTypeAdapter.FACTORY 在列表中的位置,并用自定义的工厂对象取代之。需要注意的是,必须要将工厂对象同位置替换,因为解析优先级是和列表中的位置有关的。

大功告成。

4、测试

测试代码

    @Testpublic void fromJson2() {String s = "{\"Integer\":123,\"Float\":123.0,\"String\":\"abc\",\"Boolean\":true,\"Double\":123.0}";Gson gson1 = new GsonBuilder().create();Gson gson2 = getGson();System.out.println("gson1: "+gson1.fromJson(s,new TypeToken<Map<String,Object>>(){}.getType()));System.out.println("gson2: "+gson2.fromJson(s,new TypeToken<Map<String,Object>>(){}.getType()));}

测试结果

gson1: {Integer=123.0, Float=123.0, String=abc, Boolean=true, Double=123.0}
gson2: {Integer=123, Float=123.0, String=abc, Boolean=true, Double=123.0}Process finished with exit code 0

彻底解决 Gson 将 int 转换为 double 的问题相关推荐

  1. python支持double_将int转换为double Python

    本问题已经有最佳答案,请猛点这里访问. 我似乎在这个网站上找不到答案,尽管这似乎足够普遍.我试图输出两个文件中行数之比的双精度值. 1 2 3#Number of lines in each file ...

  2. java中int转换double类型_Java 程序将int类型变量转换为double

    Java 程序将int类型变量转换为double 在此程序中,我们将学习如何在Java中将整数(int)变量转换为double值. 要理解此示例,您应该了解以下Java编程主题: 示例1:使用类型转换 ...

  3. gson 解析int类型转换为double解决方案

    背景: Gson在使用gson.fromJson对json对象中带int类型的数据转换时候,会存在nt类型转换为double的情况,原因是Gson在解释数据过程中的number类型处理导致的:具体见O ...

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

    一.探究原由 首先申明一下,我们要解决的问题有两个: Json串转Map时,int变double问题 Json串转对象时,对象属性中的Map,int变double问题 然后,我们来了解一下,Gson实 ...

  5. Java黑皮书课后题第4章:*4.26(金融应用:货币单位)重写程序清单2-10,解决将float型值转换为int型值时可能会造成精度损失的问题。读取的输入值是一个字符串,比如“11.56“

    *4.26(金融应用:货币单位)重写程序清单2-10,解决将float型值转换为int型值时可能会造成精度损失的问题.读取的输入值是一个字符串,比如"11.56" 题目 题目概述 ...

  6. c语言long int转double,将无符号long long转换为C中的double

    我意识到这个问题可能取决于处理器,但希望有人可以指出我正确的方向.对于我的生活,我无法弄清楚如何将表示纳秒的无符号long long int转换为表示C中的秒的double(我使用32位big-end ...

  7. Dart或Flutter中解决异常-type ‘int‘ is not a subtype of type ‘double‘

    文章目录 出现场景 解决方案 声明num(推荐) 判断类型 出现场景 服务端返回的金额数据可能是整数,也可能是小数. 无论我们按int或double来解析,都可能出错. 如果我们定义的类型是int,返 ...

  8. c语言算式中有double和int,C语言当中int,float,double,char这四个有什么区别?

    区别在以下方面: 一.定义方面: 1.int为整数型,用于定义整数类型的数据 . 2.float为单精度浮点型,能准确到小数点后六位 . 3.double为双精度浮点型,能准确到小数点都十二位 . 4 ...

  9. Qt中 QString 和int,double等的转换

    Qt中 int ,float ,double转换为QString 有两种方法 1.使用 QString::number(); 如: long a = 63; QString s = QString:: ...

  10. C语言的int, float,double相互转化 (从本质上理解可能的问题)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/github_33873969/article/details/78040129 从学了C语言之后,一 ...

最新文章

  1. 赛迪研究院可以入编吗_对医护来说,拥有编制很重要吗?
  2. Elasticsearch6 去重
  3. 计算机 工程领域的应用论文,工程项目管理中计算机应用论文(共2052字).doc
  4. 干货 | 当你在携程搜索时,背后的推荐系统是如何工作的
  5. ML之nyoka:基于nyoka库利用LGBMClassifier模型实现对iris数据集训练、保存为pmml模型并重新载入pmml模型进而实现推理
  6. 消息通信库ZeroMQ 4.0.4安装指南
  7. LeetCode 781. 森林中的兔子(哈希+贪心)
  8. Linux下安装Kafka(单机版)
  9. 实战Swiper:利用Swiper制作手机新闻界面
  10. 吾爱破解安卓逆向入门教程
  11. ORB SLAM2源码解读
  12. iphone win7无法识别_win7系统电脑不能识别iphone苹果设备的解决方法
  13. youtube下载视屏和字幕办法
  14. 2022年ICASSP说话人日志(Speaker Diarization)方向论文泛读总结
  15. python字典get用法_详细解析python字典get()实例教程
  16. 商城系统mysql数据表设计_购物商城数据库设计-商品表设计
  17. delphi10.2的Date/time 实用程序单元DateUtils.pas
  18. 使用CAD镜像和修剪命令绘制图形
  19. js 12:00时间加上半小时,返回小时和分钟
  20. Python中zip函数的用法

热门文章

  1. Android用户界面 UI组件--AdapterView及其子类(一) ListView及各种Adapter详解
  2. TFTP 服务器脚本
  3. 虚拟机下给Ubuntu挂载新硬盘
  4. OGNL中的#、%和$符号
  5. 项目初期不同职位的人如何沟通?以及沟通的效率
  6. 对话周鸿祎:从程序员创业谈起
  7. windows 下安装linux子系统及其可视化【Linux】
  8. openlayers5之view缩放定位
  9. 跑步进入全站 HTTPS ,这些经验值得你看看
  10. LeetCode刷刷记录