需求背景
在我們寫接口的時候可能會有這樣的需求,例如對于BigDecimal類型的字段有時候需要顯示到小數(shù)點后兩位,有時候需要顯示成百分比,有時候則取整。
@Data
public class Order {
/**
* 訂單號
*/
private String orderNo;
/**
* 訂單金額,保留兩位小數(shù)
*/
private BigDecimal orderAmount;
/**
* 優(yōu)惠金額,只保留整數(shù)
*/
private BigDecimal discountAmount;
/**
* 優(yōu)惠百分比,顯示百分比
*/
private BigDecimal discountPercent;
}
例如上面的訂單模型,我們的訂單金額需要保留兩位小數(shù),優(yōu)惠金額保留整數(shù),而優(yōu)惠百分比我們這需要以百分號的形式顯示出來,如果我們沒有做任何特殊處理,最后顯示的效果如下所示:
{
"orderNo": "20210605091",
"orderAmount": 100.05,
"discountAmount": 10.00,
"discountPercent": 0.10
}
@JsonSerialize和JsonSerializer<T>
通過查詢資料,可以通過@JsonSerialize和JsonSerializer<T>來實現(xiàn)自定義序列化。
public class PercentSerialize extends JsonSerializer<BigDecimal> {
private final DecimalFormat df = new DecimalFormat("##.00%");
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null){
gen.writeNull();
}else {
gen.writeString(df.format(value));
}
}
}
我們通過繼承JsonSerializer類來實現(xiàn)我們自己的序列化要求,這里面的<T>代表的是序列化時的類型,例如我需要對Double類型做自定義的序列化那么這個T就是Double。最后我們只要實現(xiàn)serialize方法即可,第一個參數(shù)value就是字段的值,第二個參數(shù)gen用于生成json內(nèi)容,第三個參數(shù)暫時不管。
@Data
public class Order {
/**
* 訂單號
*/
private String orderNo;
/**
* 訂單金額,保留兩位小數(shù)
*/
private BigDecimal orderAmount;
/**
* 優(yōu)惠金額,只保留整數(shù)
*/
private BigDecimal discountAmount;
/**
* 優(yōu)惠百分比,顯示百分比
*/
@JsonSerialize(using = PercentSerialize.class)
private BigDecimal discountPercent;
}
在我們的訂單模型類中我們給discountPercent字段加上@JsonSerialize(using = PercentSerialize.class)注解,然后運行代碼打印效果如下:
{
"orderNo": "20210605091",
"orderAmount": 100.05,
"discountAmount": 10.00,
"discountPercent": "10.00%"
}
存在的問題
上面貌似已經(jīng)解決了問題,我們只要再寫一個把BigDecimal顯示成整數(shù)輸出的類即可。但是如果我還要輸出別的格式怎么辦呢?難道再寫一個?看來這種方式也不是很靠譜,哪有沒有別的更好的方式呢?
如果我能獲取到序列化的那個字段,我是不是可以在字段上添加一個注解,然后讀取注解中的格式,然后在序列化時將BigDecimal序列化成我指定的格式就行了。經(jīng)過一番查找,發(fā)現(xiàn)還真有辦法可以獲取到當前序列化的字段。
Jackson中提供了一個ContextualSerializer接口,我們只要實現(xiàn)這個接口就能獲取到當前序列化的字段。
public class BigDecimalSerialize extends JsonSerializer<BigDecimal> implements ContextualSerializer {
private DecimalFormat df;
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null){
gen.writeNull();
}else {
if (df == null){
gen.writeNumber(value.setScale(2,BigDecimal.ROUND_HALF_UP));
}else {
gen.writeString(df.format(value));
}
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
//判斷beanProperty是不是空
if (property == null){
return prov.findNullValueSerializer(property);
}
//判斷類型是否是BigDecimal
if (Objects.equals(property.getType().getRawClass(),BigDecimal.class)){
JacksonBigDecimal annotation = property.getAnnotation(JacksonBigDecimal.class);
if (annotation != null){
String pattern = annotation.pattern();
df = new DecimalFormat(pattern);
return this;
}
}
return prov.findValueSerializer (property.getType (), property);
}
}
然后自定義注解如下:
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = BigDecimalSerialize.class)
public @interface JacksonBigDecimal {
/**
* BigDecimal格式,格式標準請參考{@link java.text.DecimalFormat}
*/
String pattern() default "";
}
訂單模型類中修改如下:
@Data
public class Order {
/**
* 訂單號
*/
private String orderNo;
/**
* 訂單金額,保留兩位小數(shù)
*/
@JacksonBigDecimal(pattern = "#.##")
private BigDecimal orderAmount;
/**
* 優(yōu)惠金額,只保留整數(shù)
*/
@JacksonBigDecimal(pattern = "#")
private BigDecimal discountAmount;
/**
* 優(yōu)惠百分比,顯示百分比
*/
// @JsonSerialize(using = PercentSerialize.class)
@JacksonBigDecimal(pattern = "##.00%")
private BigDecimal discountPercent;
}
最后輸出的結果如下:
{
"orderNo": "20210605091",
"orderAmount": "100.05",
"discountAmount": "10",
"discountPercent": "10.00%"
}
通過這種方式,我們在處理同一種類型時將它用不同的方式進行JSON輸出。例如我們在輸出用戶的手機號碼時,我們需要輸出前三位和后四位,中間幾位使用*號來代替。對于這種需求都可以通過這種方式實現(xiàn)。