1. 程式人生 > >jackson 實現null轉0 以及0 轉null

jackson 實現null轉0 以及0 轉null

文章目錄

需求背景

最近遇到一個需求,有個數值型別的欄位,非必填,預設為空,資料庫錶針對該欄位設計的是一個int型別, 由於dba推薦規範,預設該值是not null。這個時候,問題就來了,資料庫預設存的是0,前端展示時,又不能顯示這個0(需要的是null)

解決方案

針對此類處理,通常的方案有以下2種:

  1. 前端做處理,統一對0和null做處理,0即是null,null即是0
  2. 後端做處理,針對要處理的欄位,在序列化之前或者之後做處理,或者採取硬編碼的方式,針對要處理的欄位,寫if else

方案分析

針對第一種,這裡面有個比較尷尬的地方,前端所接收的欄位中,有些0是有意義的,譬如是否有效,0可能代表有效,如果統一做了處理,那這個改為null了,那就出問題了。假如不統一處理,則需要區別對待,由於對前端不熟,不知道是否有類似註解或者帶標誌的全域性方法來處理類似問題,聽前端說處理比較麻煩,so,只能後端來處理了。

針對第二種,後端處理的方式更靈活一些,想要簡單可拓展,使用@JasonSerilize和@JsonDeserialize註解,寫自定義序列化和反序列化類。想要快速完成,走硬編碼。起初,因為對jackson的序列化反序列化機制不太瞭解,寫的2個serializer和deserializer釋出後也問題不斷,所以為了保證專案的進展,採取了比較噁心的硬編碼的方式,寫了很多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:
    jackson 0 to null
  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);
	}

測試結果:

  1. 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。參考這些以後的序列化與反序列化類,我們可以寫出任意想要的自定義的序列化和反序列化的效果

程式碼地址