jackson 實現null轉0 以及0轉null的示例代碼
需求背景
最近遇到一個需求,有個數值類型的字段,非必填,默認為空,數據庫表針對該字段設計的是一個int類型, 由于dba推薦規(guī)范,默認該值是not null。這個時候,問題就來了,數據庫默認存的是0,前端展示時,又不能顯示這個0(需要的是null)
解決方案
針對此類處理,通常的方案有以下2種:
前端做處理,統一對0和null做處理,0即是null,null即是0
后端做處理,針對要處理的字段,在序列化之前或者之后做處理,或者采取硬編碼的方式,針對要處理的字段,寫if else
方案分析
針對第一種,這里面有個比較尷尬的地方,前端所接收的字段中,有些0是有意義的,譬如是否有效,0可能代表有效,如果統一做了處理,那這個改為null了,那就出問題了。假如不統一處理,則需要區(qū)別對待,由于對前端不熟,不知道是否有類似注解或者帶標志的全局方法來處理類似問題,聽前端說處理比較麻煩,so,只能后端來處理了。
針對第二種,后端處理的方式更靈活一些,想要簡單可拓展,使用@JasonSerilize和@JsonDeserialize注解,寫自定義序列化和反序列化類。想要快速完成,走硬編碼。起初,因為對jackson的序列化反序列化機制不太了解,寫的2個serializer和deserializer發(fā)布后也問題不斷,所以為了保證項目的進展,采取了比較惡心的硬編碼的方式,寫了很多if else來做判斷
測試序列化
maven依賴:jackson版本2.9.7
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
序列化類:
@JacksonStdImpl
public class ZeroToNullSerializer extends JsonSerializer implements ContextualSerializer {
private Class<?> type;
public ZeroToNullSerializer() {
}
public ZeroToNullSerializer(final JavaType type) {
this.type = type == null ? Object.class : type.getRawClass();
}
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (o instanceof Short) {
if (((Short) o).compareTo((short)0) == 0) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNumber(((Short) o).shortValue());
}
}
if (o instanceof Integer) {
if (((Integer) o).intValue() == 0) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNumber(((Integer) o).intValue());
}
}
if (o instanceof Float) {
if (((Float) o).compareTo(0f) == 0) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNumber(((Float) o).floatValue());
}
}
if (o instanceof Double) {
if (((Double) o).compareTo(0D) == 0) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNumber(((Double) o).doubleValue());
}
}
if (o instanceof Long) {
if (((Long) o).compareTo(0L) == 0) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNumber(((Long) o).longValue());
}
}
if (o instanceof BigDecimal) {
if (((BigDecimal) o).compareTo(BigDecimal.ZERO) == 0) {
jsonGenerator.writeNull();
}else {
jsonGenerator.writeNumber((BigDecimal) o);
}
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
return new ZeroToNullSerializer(property.getType());
}
}
測試序列化的bean:
@Data
public class TestSerializerBean {
@JsonSerialize(using =ZeroToNullSerializer.class)
private Integer number;
private Integer age;
private BigDecimal money;
}
序列化Test:
@Test
public void testSerializer() throws JsonProcessingException {
TestSerializerBean serializerBean = new TestSerializerBean();
serializerBean.setNumber(0);
serializerBean.setAge(0);
serializerBean.setMoney(new BigDecimal(20));
String string = mapper.writeValueAsString(serializerBean);
System.out.println(string);
}
測試結果:
1、待測字段帶0:

2、待測字段不帶0
@Test
public void testSerializer() throws JsonProcessingException {
TestSerializerBean serializerBean = new TestSerializerBean();
serializerBean.setNumber(10);
serializerBean.setAge(0);
serializerBean.setMoney(new BigDecimal(20));
String string = mapper.writeValueAsString(serializerBean);
System.out.println(string);
}

測試反序列化
反序列化類(僅貼出核心代碼, 完整代碼我會上傳至github, 地址見后文鏈接):
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
if (this.type == Integer.class) {
return handleInteger(p, ctxt);
}
if (this.type == Long.class) {
return handleLong(p, ctxt);
}
if (this.type == BigDecimal.class) {
return handleBigDecimal(p, ctxt);
}
if (this.type == Double.class) {
return handleDouble(p, ctxt);
}
if (this.type == Float.class) {
return handleFloat(p, ctxt);
}
if (this.type == Short.class) {
return handleShort(p, ctxt);
}
throw new RuntimeException("反序列化錯誤,類型" + type.toString() + "+不支持數值類型的反序列化");
}
private Object handleBigDecimal(JsonParser p, DeserializationContext ctxt) throws IOException {
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT:
case JsonTokenId.ID_NUMBER_FLOAT:
return p.getDecimalValue();
case JsonTokenId.ID_STRING:
String text = p.getText().trim();
// note: no need to call `coerce` as this is never primitive
if (text == null || text.length() == 0) {
return getNullValue(ctxt);
}
try {
return new BigDecimal(text);
} catch (IllegalArgumentException iae) {
}
return (BigDecimal) ctxt.handleWeirdStringValue(type, text,
"not a valid representation");
case JsonTokenId.ID_START_ARRAY:
throw new RuntimeException("NullToZeroDeserializer handleBigDecimal error, encounter token " + JsonTokenId.ID_START_ARRAY);
}
// Otherwise, no can do:
return (BigDecimal) ctxt.handleUnexpectedToken(type, p);
}
@Override
public Object getNullValue(DeserializationContext ctxt) throws JsonMappingException {
if (this.type == Integer.class) {
return 0;
}
if (this.type == BigDecimal.class) {
return BigDecimal.ZERO;
}
return 0;
}
待反序列化的bean:
@Data
public class TestDeSerializerBean{
private Integer number;
@JsonDeserialize(using = NullToZeroDeserializer.class)
private BigDecimal money;
private BigDecimal balance;
}
反序列化Test:
@Test
public void testDeSerializer() throws IOException {
TestDeSerializerBean serializerBean = new TestDeSerializerBean();
serializerBean.setNumber(5);
serializerBean.setMoney(new BigDecimal(20));
String string = mapper.writeValueAsString(serializerBean);
String testStr = "{\n" +
" \"number\": 5,\n" +
" \"money\": \"20.0\"\n" +
"}";
TestDeSerializerBean deSerializerBean = mapper.readValue(testStr, TestDeSerializerBean.class);
System.out.println(deSerializerBean);
}
測試結果:
null類型:
@Test
public void testDeSerializer() throws IOException {
TestDeSerializerBean serializerBean = new TestDeSerializerBean();
serializerBean.setNumber(5);
serializerBean.setMoney(new BigDecimal(20));
String string = mapper.writeValueAsString(serializerBean);
String testStr = "{\n" +
" \"number\": 5,\n" +
" \"money\": \"\"\n" +
"}";
TestDeSerializerBean deSerializerBean = mapper.readValue(testStr, TestDeSerializerBean.class);
System.out.println(deSerializerBean);
}

2. 非null類型

反序列化的類序列化null值時,注意要重寫 getNullValue方法
總結
以上只是針對null轉0 以及0轉null寫的代碼,當需要自定義的序列化時,往往可以參考已有的serializer 和deserializer類,譬如DateDeserializer和DateSerializer,BigDecimalDeserializer和BigDecimalSerializer。參考這些以后的序列化與反序列化類,我們可以寫出任意想要的自定義的序列化和反序列化的效果
以上這篇jackson 實現null轉0 以及0轉null的示例代碼就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java Servlet線程中AsyncContext異步處理Http請求
這篇文章主要介紹了Java Servlet線程中AsyncContext異步處理Http請求及在業(yè)務中應用,AsyncContext是Servlet 3.0使Servlet 線程不再需要一直阻塞,直到業(yè)務處理完畢才能再輸出響應,最后才結束該Servlet線程2023-03-03

