由於公司數據庫表的id是利用雪花算法生成的,所以實體類里面定義的數據類型為Long。但是這個數據傳到前端時,發生了精度丟失的現象。本文記錄了從java后端的角度如何解決這個精度丟失的問題,便於自己后續查閱。
一、問題的描述
前端通過ajax請求后端接口,返回json數據,然后將數據渲染到一個表格中。突然發現表格中id這一列出現了精度丟失的現象,這精度丟失是由前端引起的。
二、問題的解決
(1)提出方案
在后端代碼中將Long類型改為String類型即可,但是由於采用的SpringMVC框架,可簡單的做到統一處理。在輸出到頁面的Json轉換器里面做統一處理,對象序列化成json時,將Long類型變成String類型就可以了。
(2)查詢資料
由於公司里面用的是FastJson的消息轉換器,因此我們只需要擴展下
FastJsonHttpMessageConverter這個類
。
由於我們需要在fastJson在將對象序列化成Json時做處理,因此先了解下FastJson的SerializeFilter接口。該接口有好多子接口,不同類型的子接口的作用是不同的,具體如下:
SerializeFilter是通過編程擴展的方式定制序列化。fastjson支持6種SerializeFilter,用於不同場景的定制序列化。具體如下:
PropertyPreFilter 根據PropertyName判斷是否序列化
PropertyFilter 根據PropertyName和PropertyValue來判斷是否序列化
NameFilter 修改Key,如果需要修改Key,process返回值則可
ValueFilter 修改Value
BeforeFilter 序列化時在最前添加內容
AfterFilter 序列化時在最后添加內容
x
1
SerializeFilter是通過編程擴展的方式定制序列化。fastjson支持6種SerializeFilter,用於不同場景的定制序列化。具體如下:
2
3
PropertyPreFilter 根據PropertyName判斷是否序列化
4
PropertyFilter 根據PropertyName和PropertyValue來判斷是否序列化
5
NameFilter 修改Key,如果需要修改Key,process返回值則可
6
ValueFilter 修改Value
7
BeforeFilter 序列化時在最前添加內容
8
AfterFilter 序列化時在最后添加內容
(3)實戰代碼
- <1>自定義序列化Filter
由於我們需要在對象序列化成json的階段,把Long類型的Value變成String類型,因此先寫一個ValueFilter的實現類。在這實現類里面,我們把數據類型為Long的數據的Value給修改成String類型。具體代碼如下:
package com.kangxiinfo.wechat.common.fastjson;
import com.alibaba.fastjson.serializer.ValueFilter;
/**
* Long類型變成String類型——json序列化Filter
* @author ZENG.XIAO.YAN
* @time 2018-11-08 14:50:19
* @version v1.0
*/
public class LongToStringSerializeFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
if (value != null) {
// 當value不為null時,如果value是Long類型的就改成String
if (value instanceof Long) {
return value.toString();
}
}
return value;
}
}
1
package com.kangxiinfo.wechat.common.fastjson;
2
import com.alibaba.fastjson.serializer.ValueFilter;
3
4
/**
5
* Long類型變成String類型——json序列化Filter
6
* @author ZENG.XIAO.YAN
7
* @time 2018-11-08 14:50:19
8
* @version v1.0
9
*/
10
public class LongToStringSerializeFilter implements ValueFilter {
11
12
13
public Object process(Object object, String name, Object value) {
14
if (value != null) {
15
// 當value不為null時,如果value是Long類型的就改成String
16
if (value instanceof Long) {
17
return value.toString();
18
}
19
}
20
return value;
21
}
22
23
}
- <2>自定義JsonHttpMessageConverter
直接繼承
FastJsonHttpMessageConverter
這個類,然后重寫
writeInternal
這個方法。在writeInternal方法里面進行對象序列成json串時,傳入我們上面自定義的序列化Filter。具體代碼如下:
package com.kangxiinfo.wechat.common.fastjson;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
public class CustomFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {
public final static Charset UTF8 = Charset.forName("UTF-8");
private Charset charset = UTF8;
private SerializerFeature[] features = new SerializerFeature[0];
/** 自定義的filter */
private LongToStringSerializeFilter longToStringSerializeFilter = new LongToStringSerializeFilter();
public CustomFastJsonHttpMessageConverter() {
super();
}
@Override
protected boolean supports(Class<?> clazz) {
return true;
}
public Charset getCharset() {
return this.charset;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public SerializerFeature[] getFeatures() {
return features;
}
public void setFeatures(SerializerFeature... features) {
this.features = features;
}
// 重寫這個方法
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
OutputStream out = outputMessage.getBody();
// 轉json時 加入自定義的Filter
String text = JSON.toJSONString(obj, longToStringSerializeFilter, features);
byte[] bytes = text.getBytes(charset);
out.write(bytes);
}
}
1
package com.kangxiinfo.wechat.common.fastjson;
2
import java.io.IOException;
3
import java.io.OutputStream;
4
import java.nio.charset.Charset;
5
import org.springframework.http.HttpOutputMessage;
6
import org.springframework.http.converter.HttpMessageNotWritableException;
7
import com.alibaba.fastjson.JSON;
8
import com.alibaba.fastjson.serializer.SerializerFeature;
9
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
10
11
public class CustomFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {
12
public final static Charset UTF8 = Charset.forName("UTF-8");
13
private Charset charset = UTF8;
14
private SerializerFeature[] features = new SerializerFeature[0];
15
/** 自定義的filter */
16
private LongToStringSerializeFilter longToStringSerializeFilter = new LongToStringSerializeFilter();
17
18
public CustomFastJsonHttpMessageConverter() {
19
super();
20
}
21
22
23
protected boolean supports(Class<?> clazz) {
24
return true;
25
}
26
27
public Charset getCharset() {
28
return this.charset;
29
}
30
31
public void setCharset(Charset charset) {
32
this.charset = charset;
33
}
34
35
public SerializerFeature[] getFeatures() {
36
return features;
37
}
38
39
public void setFeatures(SerializerFeature... features) {
40
this.features = features;
41
}
42
43
44
// 重寫這個方法
45
46
protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
47
throws IOException, HttpMessageNotWritableException {
48
OutputStream out = outputMessage.getBody();
49
// 轉json時 加入自定義的Filter
50
String text = JSON.toJSONString(obj, longToStringSerializeFilter, features);
51
byte[] bytes = text.getBytes(charset);
52
out.write(bytes);
53
}
54
}
55
- <3>在SpringMVC配置文件中配置使用我們自定義的Json轉換器
配置的具體代碼如下:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="false">
<!-- 注冊我們擴展了的fastjson轉換器 -->
<bean class="com.kangxiinfo.wechat.common.fastjson.CustomFastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
<value>text/plain;charset=UTF-8</value>
<value>application/x-www-form-urlencoded;charset=UTF-8</value>
</list>
</property>
<property name="features">
<list>
<value>WriteMapNullValue</value>
<value>WriteNullListAsEmpty</value>
<value>WriteNullStringAsEmpty</value>
<value>WriteNullNumberAsZero</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
1
<mvc:annotation-driven>
2
<mvc:message-converters register-defaults="false">
3
<!-- 注冊我們擴展了的fastjson轉換器 -->
4
<bean class="com.kangxiinfo.wechat.common.fastjson.CustomFastJsonHttpMessageConverter">
5
<property name="supportedMediaTypes">
6
<list>
7
<value>text/html;charset=UTF-8</value>
8
<value>application/json;charset=UTF-8</value>
9
<value>text/plain;charset=UTF-8</value>
10
<value>application/x-www-form-urlencoded;charset=UTF-8</value>
11
</list>
12
</property>
13
<property name="features">
14
<list>
15
<value>WriteMapNullValue</value>
16
<value>WriteNullListAsEmpty</value>
17
<value>WriteNullStringAsEmpty</value>
18
<value>WriteNullNumberAsZero</value>
19
</list>
20
</property>
21
</bean>
22
</mvc:message-converters>
23
</mvc:annotation-driven>
三、小結
(1)Long類型數據傳到前端精度丟失時,可以將Long類型轉成String類型再輸出到前端就不會丟失精度(2)可以通過擴展SpringMVC的消息轉換器,全局處理這種Long類型數據精度丟失的問題
