揭秘前端精度丢失之谜!!!

今天测试突然跟我说页面显示的数值和数据库的对应不上,一开始我以为是程序问题,把数据给修改了,但是后面检查程序发现,没有任务问题,用postman请求,发现数据也和数据库的一致。但是页面上面显示的就是不一样。

问题追踪

一开始我怀疑是前端转型把精度丢失,前端那边直接是通过Number接收的,没有任何转型操作。后来我怀疑可能是因为服务器是Long类型,前端是Number类型,可能是精度对应不上,后面一查资料发现还真是这个问题。

Number和Long精度

Number的最大长度是2的53次,也就是9007199254740992,而Long的最大长度是2的64次,也就是9223372036854775807。所以会出现精度丢失的情况。

解决方案

要解决这个问题其实不难,只要将Long类型变为String类型即可,但是我们不可能直接将字段修改为String,这样会涉及很多模块代码的修改,工作量和安全性都会受到影响。

还有一种办法是重新再添加一个新的字段,然后get的时候将需要的字段转为字符串返回即可,如下所示:

    private Long userId;
    private String userIdStr;
    public String getUserIdStr() {
        return this.userId+"";
    }
    public void setUserIdStr(String userIdStr) {
        this.userIdStr = userIdStr;
    }

但是这样一方面需要修改dto对象的字段,另一方面前端也需要相应的修改,如果有很多地方需要这样做,那工作量也是非常大的,所以这种方式也是不可取的。

方案筛选

最理想的方案应该是不修改字段任何属性,因为这样前端就不需要变动。(如果前端指定具体接收类型,那就需要修改),在序列化返回给前端的时候将Long类型的字段转为String类型。

这边就给大家介绍一下jackson是如何进行序列化和反序列化的。

首先我们来看一下没有进行任何序列化修改,Long类型怎么输出的?我们先来看一下代码。

@RequestMapping(value = "testDemo", method = RequestMethod.GET)
    @ResponseBody
    public TestDto testDemo() {
        TestDto testDto = new TestDto();
        testDto.setId(Long.valueOf("123456789012345678"));
        return testDto;
    }
    
@Data
public class TestDto {
    /**
     * ID标识
     */
    private Long id;
}

接下来我们用postman请求一下,看看返回的结果是啥样的?

{
    "id": 123456789012345678
}

我们可以看到String类型的返回结果是没有双引号或者单引号的,我们修改一下id字段的类型(从Long类型修改为String),看看输出结果有什么不一样。

@Data
public class TestDto {
    /**
     * ID标识
     */
    private String id;
}

输出结果:

{
    "id": "123456789012345678"
}

我们可以很明显的看到,字段id值是有双引号的。那我们如何在不修改任何代码的情况下,返回值值从Long类型修改为Stirng,返回给前端。

接下来就是重头戏了,我们先写一个Annotation注解,如下所示:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside
@JsonSerialize(using = LongToStringSerializer.class)
public @interface LongToString {
}

不懂@Retention和@Target含义的童鞋,可以自行百度。

重点在LongToStringSerializer类中,该类的作用是进行json序列化,它有一个serialize方法,允许我们对序列化的字段进行修改,具体我们来看一下代码:

public class LongToStringSerializer extends JsonSerializer<Object> {
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(String.valueOf(value));
    }
}

这边我们将原来是Long类型的字段,重写为String类型,最后我们用postman来测试一下,返回结果是否包含双引号。

{
    "id": "123456789012345678"
}

从结果我们可以很明显的看出,id字段的类型是String,期间我们没有修改id字段的类型,但是通过jackson序列化的时候进行字段类型重写,就可以轻轻松松实现类型的修改。

总结

用法虽然很简单,但是这其中过程是怎么样的呢,到底在什么时候会进行对象序列化输出?我们下一节来具体给大家介绍一下。今天文章分享就到这边,谢谢童鞋们的阅读~

想要更多干货、技术猛料的孩子,快点拿起手机扫码关注我,我在这里等你哦~

林老师带你学编程https://wolzq.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章