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

这篇具有很好参考价值的文章主要介绍了Gson int类型被转换成double问题解决(完美解决)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、问题复现

1.1、BaseResponse类

class BaseResponse<T>(
    val code: Int = -1,
    val message: String? = null
) {
    var data: T? = null

    fun 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")
        @Override
        public <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;
    }

    @Override
    public 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();
        }
    }

    @Override
    public void write(JsonWriter out, Object value) throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        //noinspection unchecked
        TypeAdapter<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;
  }

  @Override
  public int intValue() {
    try {
      return Integer.parseInt(value);
    } catch (NumberFormatException e) {
      try {
        return (int) Long.parseLong(value);
      } catch (NumberFormatException nfe) {
        return new BigDecimal(value).intValue();
      }
    }
  }

  @Override
  public long longValue() {
    try {
      return Long.parseLong(value);
    } catch (NumberFormatException e) {
      return new BigDecimal(value).longValue();
    }
  }

  @Override
  public float floatValue() {
    return Float.parseFloat(value);
  }

  @Override
  public double doubleValue() {
    return Double.parseDouble(value);
  }

  @Override
  public 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 replacement
    throw new InvalidObjectException("Deserialization is unsupported");
  }

  @Override
  public int hashCode() {
    return value.hashCode();
  }

  @Override
  public 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天,最后还是完美解决文章来源地址https://www.toymoban.com/news/detail-501356.html

到了这里,关于Gson int类型被转换成double问题解决(完美解决)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • BigDecimal和double类型相互转换

    2024年02月06日
    浏览(36)
  • char类型与int类型的转换

        在c语言中,char类型与int类型可以转换,如何转换我在此做一个粗略的总结 首先是char转换为int 结果为:n 110 110 即将ASC码强制转换为int 但当输入的数字大于127(ASC码的最大值)时,-128和127就像转盘一样连在一起,如果上述代码中a=128,则输出   一个意义不明的符号(因

    2024年02月11日
    浏览(40)
  • 天坑!python【numpy.int64和int类型转换】

    numpy.int64和int是不一样的!!!!一定要注意,这个坑我踩了好几天才发现!!!!! 比如说 :在处理dataframe的时候,读取出来的可能是numpy.int64,但是后边的程序需要int类型,你把numpy.int64喂给它,就会导致程序运行不出你想要的结果,但又不报错。这个时候可以检查一下

    2024年02月16日
    浏览(31)
  • C语言字符串转换double等类型(sscanf,atof,strod)

    例子: 注意: 忽略空格   例子: 注意: sscanf解析字符串时,空格通常被用作分隔符,可以用它来分隔字符串中的不同部分。 例如,以下代码将把字符串\\\"123 456\\\"中的两个整数读入a和b中,空格用作分隔符: 在这个例子中,sscanf会忽略字符串中的多余空格,并将\\\"123\\\"解析为a的

    2024年02月14日
    浏览(34)
  • java中char类型转换成int类型的方法

    java中,需要对输入进行一些判断,比如需要输入的是数字,而用户输入了字符,那么就会报错,因此用char或者String类型接收输入的数据就不会报错,但是问题来了:如何让输入的char或者String类型变为数字呢? 以下是一些方法: 第一种利用Integer 包装类 的方法 Integer.parseIn

    2024年02月13日
    浏览(40)
  • Java 中将 String 类型转换为 int 类型的方法

    在Java中将 String 类型转换为 int 类型是一个常见的操作,因为在实际开发中,我们经常需要从用户输入或者外部数据源中获取字符串形式的数字,并将其转换为整数进行计算和处理。在Java中,有几种方法可以实现这种转换,下面我将逐一介绍这些方法。 Integer.parseInt() 方法是

    2024年04月22日
    浏览(36)
  • Java中将String类型转换为int类型的方法

    在Java编程中,经常需要将字符串类型(String)转换为整数类型(int)。这在处理用户输入、读取文件或进行数值计算时非常常见。本文将详细介绍如何在Java中进行这种类型转换,并提供相应的源代码示例。 Java提供了几种方法来实现String到int的转换。下面将介绍其中的两种常

    2024年02月05日
    浏览(72)
  • C语言中的char类型和int类型的相互转换

    C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植等特点。C语言中有多种数据类型,用来表示不同的数据和信息。本文将介绍C语言中的两种基本数据类型:char类型和int类型,以及它们之间的相互转换的方法和原理。 char类型是一种字符类型,用来表示单个字符,

    2024年02月03日
    浏览(40)
  • C++中string类型和int类型之间的相互转换【全网最全】

    字符串操作是各种算法题中的常客,很多数据常常以字符串形式给出,其中有的需要自己转化成整数,而一些整型数据有时转换成字符串处理起来更加方便,比如判断一个整数是否是回文数,所以字符串和整数的转换是一些问题处理的基础步骤, C++  在处理这类问题时并不像

    2024年02月04日
    浏览(55)
  • python中str与int类型的相互转换

    1. str转换成int 方法:使用int()函数 3. int转换成str 方法:使用str()函数

    2024年02月13日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包