1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Gson int类型被转换成double问题解决(完美解决)

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

时间:2023-12-03 02:14:11

相关推荐

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

一、问题复现

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.;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 serialVersionUIDpublic 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天,最后还是完美解决

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。