问题描述
项目中同步订单时发现一个问题,同一条的数据,order_id和item_id为Long类型,在同步时,数值变了。比如原本是6930414387088791188变成了69304143870884512001。
问题排查
经过排查发现http请求的返回参数是正常的,问题出现在客户端调用接口得到结果后,在进行json转换时,出现问题。项目中使用的gson作为json序列化和反序列化的工具。
转换代码如下:
Result result = JsonUtil.jsonToObject(httpResult.getBody(), Result.class);
List<OrderResponse> list =JsonUtil.jsonToList(JsonUtil.objectToJson(result), OrderResponse.class);
这个转换,第一步会使用gson默认的object转换:将数值类型转换成了Double类型。第二步再转换为指定类型时,数值就变了。
查看源码发现,只要是源数据是数值类型,在不指定类型的情况进行转换时,会使用gson默认的转换ObjectTypeAdapter,将所有数值型转换为Double,当源数据的Long型长度为19位(超过16位),丢失了精度,导致数据发生了变化。
解决方案
- **(推荐)**转换时制定好类型,不用gson默认的转换。转换方法如下:
返回类定义如下:
public class ResponseList<T> {
private String code;//状态码
private String msg;//处理消息
private List<T> data;
...
}
JsonUtil中转换方法如下:
public static <T> T fromJson(String json, Class<T> clazz, Class type) {
if (StringUtils.isBlank(json)) {
return null;
}
Type objectType = type(clazz, type);
return gson.fromJson(json, objectType);
}
private static ParameterizedType type(final Class raw, final Type... args) {
return new ParameterizedType() {
public Type getRawType() {
return raw;
}
public Type[] getActualTypeArguments() {
return args;
}
public Type getOwnerType() {
return null;
}
};
}
调用http结果后json转实体如下:
ResponseList<OrderResponse> resultResponse = JsonUtil.fromJson(httpResult.getBody(), ResponseList.class, OrderResponse.class);
- 如果继续想使用gson默认的转换,升级gson版本>=2.8.9,设置默认的转换策略,用BigDecimal接收,亲测可用
升级gson版本至2.8.9以上
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
public class JsonUtil {
private static Gson gson = new Gson();
static {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL);
gson = gsonBuilder.create();
}
}