一、问题复现

1.1、BaseResponse类

class BaseResponse<T>(val code: Int = -1,val message: String? = null
) {var data: T? = nullfun isSuccess(): Boolean {return code == 0}
}

1.2、不做任何处理,直接用Gson.fromJson解析

val json = "{\"code\":200,\"message\":\"成功\",\"data\":{\"ints\":200,\"doubles\":200.98,\"floats\":29.0986,\"longs\":29323627832875342,\"string\":\"字符串\"}}"
val baseResponse = Gson().fromJson(json, BaseResponse::class.java)
println(JsonUtils.toJson(baseResponse))

1.3、解析结果

I/System.out: {"code":200,"data":{"ints":200.0,"doubles":200.98,"floats":29.0986,"longs":2.9323627832875344E16,"string":"字符串"},"message":"成功"}

1.4、问题

Int类型的"ints"被转换成了double类型、Long类型的"longs"也被转换了

二、解决方案

2.1、旧的(网上千篇一律的复制方案,其实没有任何卵用)

网上千篇一律就是新建TypeAdapter接口的子类,手动转换。MyDataTypeAdapter

package com.xxx.baseapp.net;import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.ToNumberPolicy;
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 java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;public final class MyDataTypeAdapter 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 MyDataTypeAdapter(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);}/*** 使用自定义工厂方法取代 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.getFactory(ToNumberPolicy.DOUBLE));list.set(i, MyDataTypeAdapter.FACTORY);break;}}} catch (Exception e) {e.printStackTrace();}return gson;}}

2.2、修改方案(看了源码就快要解决了)

2.2.1、分析

通过分析GsonBuilder的构造方法源码:

   /*** Creates a GsonBuilder instance that can be used to build Gson with various configuration* settings. GsonBuilder follows the builder pattern, and it is typically used by first* invoking various configuration methods to set desired options, and finally calling* {@link #create()}.*/public GsonBuilder() {}/*** Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder* has the same configuration as the previously built Gson instance.** @param gson the gson instance whose configuration should by applied to a new GsonBuilder.*/GsonBuilder(Gson gson) {......this.objectToNumberStrategy = gson.objectToNumberStrategy;this.numberToNumberStrategy = gson.numberToNumberStrategy;this.reflectionFilters.addAll(gson.reflectionFilters);}/*** Configures Gson to apply a specific number strategy during deserialization of {@link Object}.** @param objectToNumberStrategy the actual object-to-number strategy* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern* @see ToNumberPolicy#DOUBLE The default object-to-number strategy* @since 2.8.9*/public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy) {this.objectToNumberStrategy = Objects.requireNonNull(objectToNumberStrategy);return this;}

可以发现,在构造的时候默认了Object和Number类转换成数字类型的处理策略,可以看到,Object转换默认为ToNumberPolicy.DOUBLE的dobule的类型,因此不单单是int会被转换成double,Long类型等等数值类型都会默认被转换成dobule类型。

2.2.2、策略类

/*** An enumeration that defines two standard number reading strategies and a couple of* strategies to overcome some historical Gson limitations while deserializing numbers as* {@link Object} and {@link Number}.** @see ToNumberStrategy* @since 2.8.9*/
public enum ToNumberPolicy implements ToNumberStrategy {/*** Using this policy will ensure that numbers will be read as {@link Double} values.* This is the default strategy used during deserialization of numbers as {@link Object}.*/DOUBLE {@Override public Double readNumber(JsonReader in) throws IOException {return in.nextDouble();}},/*** Using this policy will ensure that numbers will be read as a lazily parsed number backed* by a string. This is the default strategy used during deserialization of numbers as* {@link Number}.*/LAZILY_PARSED_NUMBER {@Override public Number readNumber(JsonReader in) throws IOException {return new LazilyParsedNumber(in.nextString());}},/*** Using this policy will ensure that numbers will be read as {@link Long} or {@link Double}* values depending on how JSON numbers are represented: {@code Long} if the JSON number can* be parsed as a {@code Long} value, or otherwise {@code Double} if it can be parsed as a* {@code Double} value. If the parsed double-precision number results in a positive or negative* infinity ({@link Double#isInfinite()}) or a NaN ({@link Double#isNaN()}) value and the* {@code JsonReader} is not {@link JsonReader#isLenient() lenient}, a {@link MalformedJsonException}* is thrown.*/LONG_OR_DOUBLE {@Override public Number readNumber(JsonReader in) throws IOException, JsonParseException {String value = in.nextString();try {return Long.parseLong(value);} catch (NumberFormatException longE) {try {Double d = Double.valueOf(value);if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) {throw new MalformedJsonException("JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath());}return d;} catch (NumberFormatException doubleE) {throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE);}}}},/*** Using this policy will ensure that numbers will be read as numbers of arbitrary length* using {@link BigDecimal}.*/BIG_DECIMAL {@Override public BigDecimal readNumber(JsonReader in) throws IOException {String value = in.nextString();try {return new BigDecimal(value);} catch (NumberFormatException e) {throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), e);}}}}

通过上面的策略类源码,我们发现:LAZILY_PARSED_NUMBER 就是处理数值类型的枚举,查看LazilyParsedNumber源码,发现确实进行了细致的数值类型处理划分:

/*** This class holds a number value that is lazily converted to a specific number type** @author Inderjeet Singh*/
@SuppressWarnings("serial") // ignore warning about missing serialVersionUID
public final class LazilyParsedNumber extends Number {private final String value;/** @param value must not be null */public LazilyParsedNumber(String value) {this.value = value;}@Overridepublic int intValue() {try {return Integer.parseInt(value);} catch (NumberFormatException e) {try {return (int) Long.parseLong(value);} catch (NumberFormatException nfe) {return new BigDecimal(value).intValue();}}}@Overridepublic long longValue() {try {return Long.parseLong(value);} catch (NumberFormatException e) {return new BigDecimal(value).longValue();}}@Overridepublic float floatValue() {return Float.parseFloat(value);}@Overridepublic double doubleValue() {return Double.parseDouble(value);}@Overridepublic String toString() {return value;}/*** If somebody is unlucky enough to have to serialize one of these, serialize* it as a BigDecimal so that they won't need Gson on the other side to* deserialize it.*/private Object writeReplace() throws ObjectStreamException {return new BigDecimal(value);}private void readObject(ObjectInputStream in) throws IOException {// Don't permit directly deserializing this class; writeReplace() should have written a replacementthrow new InvalidObjectException("Deserialization is unsupported");}@Overridepublic int hashCode() {return value.hashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (obj instanceof LazilyParsedNumber) {LazilyParsedNumber other = (LazilyParsedNumber) obj;return value == other.value || value.equals(other.value);}return false;}
}

2.2.2、最终解决方案

既然知道构造的时候设置了默认的 ToNumberPolicy.DOUBLE ,那修改起来就简单了,我们调用设置转换的策略方法即可:

fun getGson(): Gson {val gsonBuilder = GsonBuilder()gsonBuilder.setObjectToNumberStrategy(ToNumberPolicy.LAZILY_PARSED_NUMBER)return gsonBuilder.create()
}

调用修改策略方案后的getGson解析

val json = "{\"code\":200,\"message\":\"成功\",\"data\":{\"ints\":200,\"doubles\":200.98,\"floats\":29.0986,\"longs\":29323627832875342,\"string\":\"字符串\"}}"
val baseResponse = getGson().fromJson(json, BaseResponse::class.java)
println(JsonUtils.toJson(baseResponse))

测试结果:

I/System.out: {"code":200,"data":{"ints":200,"doubles":200.98,"floats":29.0986,"longs":29323627832875342,"string":"字符串"},"message":"成功"}

至此完美解决!!!!

网上千篇一律的复制粘贴说这可以解决那个可以解决,结果然并卵。这个问题困扰了我2天,最后还是完美解决

Gson int类型被转换成double问题解决(完美解决)相关推荐

  1. php 时间类型int类型,mysql 查询 int类型日期转换成datetime类型

    1,数据库日期类型是int类型的,该查询结果是datetime类型的 SELECT from_unixtime( `dateline` ) FROM cdb_posts 2,如果原来类型是dateti ...

  2. 【Java】int类型强制转换成long

    问题 因为项目中有的地方需要转换数据类型 这次的是 Int -> Long 解决方法 Long.valueOf(int); 失败了.... (long) myInt; 失败了... Long.p ...

  3. python double转int_如何将int转换成double

    展开全部 将int转换成double可以使用强制62616964757a686964616fe59b9ee7ad9431333431353431类型转换.自动类型转换两种方式. 1.强制类型转换 强制 ...

  4. java long转换double类型_Java 将Long转换成Double类型

    Java 将Long转换成Double类型,其实很简单,调用Long类型的Long.doubleValue(): // 将数据库获取的数据进行拼接成一个月数据 public static List g ...

  5. java整型转换为数组_基于java中byte数组与int类型的转换(两种方法)

    java中byte数组与int类型的转换,在网络编程中这个算法是最基本的算法,我们都知道,在socket传输中,发送.者接收的数据都是 byte数组,但是int类型是4个byte组成的,如何把一个整形 ...

  6. java中byte数组与int类型的转换(两种方式)

    java中byte数组与int类型的转换,在网络编程中这个算法是最基本的算法,我们都知道,在socket传输中,发送.者接收的数据都是 byte数组,但是int类型是4个byte组成的,如何把一个整形 ...

  7. c语言整数转ip地址字符串,C/C++ ip地址与int类型的转换实例详解

    C/C++ ip地址与int类型的转换实例详解 前言 最近看道一个面试题目,大体意思就是将ip地址,例如"192.168.1.116"转换成int类型,同时还能在转换回去 思路 i ...

  8. char类型与int类型的转换

    在c语言中,char类型与int类型可以转换,如何转换我在此做一个粗略的总结 首先是char转换为int #include <stdio.h> int main() {char a ;a ...

  9. byte数组与int类型互相转换的几种方式

    查看原文:http://www.ibloger.net/article/147.html Java中byte数组与int类型的转换,在网络编程中这个算法是最基本的算法,我们都知道,在socket传输中 ...

最新文章

  1. TCP连接的建立和终止
  2. 大工18秋《计算机网络技术》在线作业1,大工18秋《专业英语(计算机英语)》在线作业3【标准答案】...
  3. WPF获取当前用户控件的父级窗体
  4. 关于软件项目中的风险
  5. 墨天轮章芋文:用十年打造中国的数据库生态体系
  6. python入门指南txt-BeginnersGuide
  7. flow-shop调度问题、job shop调度问题、open shop调度问题 是什么 区别
  8. PYQT窗口可视化编程
  9. 经理人必看的8大网站
  10. http(S)系列之(五):android之HttpURLConnection源码解析(1)
  11. java为什么 foreach比for效率高
  12. java web 精仿微博_【Java】盘点 Github 上的高仿 app 项目,B站 微博 微信等等
  13. 2022-2027年中国安全仪表系统(SIS)行业发展监测及投资战略研究报告
  14. 爬虫工具 AppCrawler
  15. 我爱天文 - 秋季有大三角吗?
  16. PHP+ajaxfileupload 实现用户头像上传
  17. 苹果cms如何在虚拟主机上设置伪静态
  18. 数据模型(LP32 ILP32 LP64 LLP64 ILP64 )
  19. 已知一点经纬度和距离,计算另一点的经纬度
  20. zigbee_蓝牙_wifi的比较与区别分析

热门文章

  1. IO流的详解,彻底了解IO流
  2. pfx证书导入方式,步骤流程
  3. 用 HTTPS 安全吗?HTTPS 的原理是啥?
  4. 二维数组与指向指针的指针
  5. Android监听电池电量
  6. 图像处理相关算法之饱和度调整
  7. Android反编译工具与实践
  8. 高性能Golang研讨会【精】
  9. MSYS+MinGW64环境的搭建
  10. LruCache的深入解析