由js精度缺失引发的问题及处理方法

问题场景

在使用java的Long类型过程中,当这个Long类型数值超过 1^0+1^1+1^2+...1^58的范围后就会引发精度丢失, 详见JS[IEEE754规范](https://en.wikipedia.org/wiki/IEEE_754)
常见的现象就是比如:1000967854800048128,通过js转换成Number类型成为了:1000967854800048100, 由此会引发一系列问题,比如当年著名的爱国者导弹.
因为精度缺失造成问题,大多数情况发生在前后端交互过程中,那么下面就分别说说处理方法

普通场景的处理方法

简单的处理方法,jackson,gson等json工具包已经包含了Long类型处理方法了,基本上都是转String.

gson处理方法

GsonBuilder builder = new GsonBuilder();
//默认将所有的Long转换成String
builder.setLongSerializationPolicy(LongSerializationPolicy.STRING);

jackson处理方法

ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);

高级场景处理

为了避免将其他无需转换成String的Long类型转换,这里提供一个高级的用法,
思路很简单就是定义一个Id类型,通过实现Id类型的序列化和反序列化功能即可进行实现.
下面就用Gson和jackson来演示

核心代码

    //Gson
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Id.class, (JsonSerializer) (src, typeOfSrc, context) ->
            new JsonPrimitive(src.getVal()));
    builder.registerTypeAdapter(Id.class, (JsonDeserializer) (json, typeOfT, context) ->
            new Id(json.getAsLong()));
    Gson gson = builder.create();
    User user = new User();
    user.setUserId(new Id(11111L));
    user.setUserName("youkale.github.io");

    String s = gson.toJson(user);
    System.out.println(s);

    User user1 = gson.fromJson(s, User.class);
    System.out.println("gson  " + user1);

    //Jackson
    ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule module = new SimpleModule();
    module.addSerializer(Id.class, new IdJacksonSerializer(Id.class));
    module.addDeserializer(Id.class, new IdJacksonDeserializer(Id.class));
    objectMapper.registerModule(module);
    String s1 = objectMapper.writeValueAsString(user);
    System.out.println(s1);
    User user2 = objectMapper.readValue(s1, User.class);
    System.out.println("jackson  " + user2);

定义类User

public static class User {

    private Id userId;

    private String userName;

    public Id getUserId() {
        return userId;
    }

    public void setUserId(Id userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
}

定义Id类

public static class Id {

    private final Long val;

    public Long getVal() {
        return val;
    }

    public Id(Long val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return String.valueOf(val);
    }
}

jackson序列反序列实现

public static class IdJacksonDeserializer extends StdDeserializer {
    public IdJacksonDeserializer(Class vc) {
        super(vc);
    }

    @Override
    public Id deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return new Id(p.getValueAsLong());
    }
}

public static class IdJacksonSerializer extends StdSerializer {

    public IdJacksonSerializer(Class t) {
        super(t);
    }
    @Override
    public void serialize(Id value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeString(String.valueOf(value.getVal()));
    }
}

总结

从Gson和jackson的API看出这类的功能老早就由大神们想到了,有时候总在感叹,自己多渺小.

你可能感兴趣的:(由js精度缺失引发的问题及处理方法)