需求背景
在我們寫接口的時(shí)候可能會(huì)有這樣的需求合搅,例如對(duì)于BigDecimal類型的字段有時(shí)候需要顯示到小數(shù)點(diǎn)后兩位兢哭,有時(shí)候需要顯示成百分比不铆,有時(shí)候則取整暂题。
@Data
public class Order {
/**
* 訂單號(hào)
*/
private String orderNo;
/**
* 訂單金額,保留兩位小數(shù)
*/
private BigDecimal orderAmount;
/**
* 優(yōu)惠金額丑慎,只保留整數(shù)
*/
private BigDecimal discountAmount;
/**
* 優(yōu)惠百分比喜喂,顯示百分比
*/
private BigDecimal discountPercent;
}
例如上面的訂單模型瓤摧,我們的訂單金額需要保留兩位小數(shù),優(yōu)惠金額保留整數(shù)玉吁,而優(yōu)惠百分比我們這需要以百分號(hào)的形式顯示出來(lái)照弥,如果我們沒(méi)有做任何特殊處理,最后顯示的效果如下所示:
{
"orderNo": "20210605091",
"orderAmount": 100.05,
"discountAmount": 10.00,
"discountPercent": 0.10
}
@JsonSerialize和JsonSerializer<T>
通過(guò)查詢資料进副,可以通過(guò)@JsonSerialize和JsonSerializer<T>來(lái)實(shí)現(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));
}
}
}
我們通過(guò)繼承JsonSerializer類來(lái)實(shí)現(xiàn)我們自己的序列化要求,這里面的<T>代表的是序列化時(shí)的類型影斑,例如我需要對(duì)Double類型做自定義的序列化那么這個(gè)T就是Double给赞。最后我們只要實(shí)現(xiàn)serialize方法即可,第一個(gè)參數(shù)value就是字段的值矫户,第二個(gè)參數(shù)gen用于生成json內(nèi)容片迅,第三個(gè)參數(shù)暫時(shí)不管。
@Data
public class Order {
/**
* 訂單號(hào)
*/
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)注解,然后運(yùn)行代碼打印效果如下:
{
"orderNo": "20210605091",
"orderAmount": 100.05,
"discountAmount": 10.00,
"discountPercent": "10.00%"
}
存在的問(wèn)題
上面貌似已經(jīng)解決了問(wèn)題膳汪,我們只要再寫一個(gè)把BigDecimal顯示成整數(shù)輸出的類即可唯蝶。但是如果我還要輸出別的格式怎么辦呢?難道再寫一個(gè)遗嗽?看來(lái)這種方式也不是很靠譜粘我,哪有沒(méi)有別的更好的方式呢?
如果我能獲取到序列化的那個(gè)字段痹换,我是不是可以在字段上添加一個(gè)注解征字,然后讀取注解中的格式,然后在序列化時(shí)將BigDecimal序列化成我指定的格式就行了娇豫。經(jīng)過(guò)一番查找匙姜,發(fā)現(xiàn)還真有辦法可以獲取到當(dāng)前序列化的字段。
Jackson中提供了一個(gè)ContextualSerializer接口冯痢,我們只要實(shí)現(xiàn)這個(gè)接口就能獲取到當(dāng)前序列化的字段氮昧。
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格式,格式標(biāo)準(zhǔn)請(qǐng)參考{@link java.text.DecimalFormat}
*/
String pattern() default "";
}
訂單模型類中修改如下:
@Data
public class Order {
/**
* 訂單號(hào)
*/
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;
}
最后輸出的結(jié)果如下:
{
"orderNo": "20210605091",
"orderAmount": "100.05",
"discountAmount": "10",
"discountPercent": "10.00%"
}
通過(guò)這種方式,我們?cè)谔幚硗环N類型時(shí)將它用不同的方式進(jìn)行JSON輸出振劳。例如我們?cè)谳敵鲇脩舻氖謾C(jī)號(hào)碼時(shí)椎组,我們需要輸出前三位和后四位,中間幾位使用*號(hào)來(lái)代替历恐。對(duì)于這種需求都可以通過(guò)這種方式實(shí)現(xiàn)寸癌。