问题描述
最近在使用一个内部的RPC框架时,发现如果使用Object类型,实际类型为BigDecimal的时候,作为传输对象的时候,会出现丢失精度的问题;比如在序列化前为金额1.00,反序列化之后为1.0,本身值可能没有影响,但是在有些强依赖金额的地方,会出现问题;
问题分析
查看源码发现RPC框架默认使用的序列化框架为Jackson,那简单,看一下本地是否可以重现问题;
1.准备数据传输bean
public class Bean1 {
private String p1;
private BigDecimal p2;
...省略get/set...
}
public class Bean2 {
private String p1;
private Object p2;
...省略get/set...
}
为了更好的看出问题,分别准备了2个bean;
2.准备测试类
public class JKTest {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Bean1 bean1 = new Bean1("haha1", new BigDecimal("1.00"));
Bean2 bean2 = new Bean2("haha2", new BigDecimal("2.00"));
String bs1 = mapper.writeValueAsString(bean1);
String bs2 = mapper.writeValueAsString(bean2);
System.out.println(bs1);
System.out.println(bs2);
Bean1 b1 = mapper.readValue(bs1, Bean1.class);
System.out.println(b1.toString());
Bean2 b22 = mapper.readValue(bs2, Bean2.class);
System.out.println(b22.toString());
}
}
分别对Bean1和Bean2进行序列化和反序列化操作,然后查看结果;
3.显示结果
{"p1":"haha1","p2":1.00}
{"p1":"haha2","p2":2.00}
Bean1 [p1=haha1, p2=1.00]
Bean2 [p1=haha2, p2=2.0]
4.结果分析
结果可以发现两个问题:
1.在序列化的时候2个bean都没有问题;
2.重现了问题,Bean2在反序列化时,p2出现了精度丢失的问题;
5.源码分析
通过一步一步查看Jackson源码,最终定位到UntypedObjectDeserializer的Vanilla内部类中,反序列方法如下:
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_START_OBJECT:
{
JsonToken t = p.nextToken();
if (t == JsonToken.END_OBJECT) {
return new LinkedHashMap(2);
}
}
case JsonTokenId.ID_FIELD_NAME:
return mapObject(p, ctxt);
case J