简介
最近做项目时涉及大量对数字需要进行精度的控制,产品设计的各功能模块小数点后保留位数不一样,数据库设计的DECIMAL类型.
对于接口设计前端同学肯定要求这个精度后端处理的,以往都是手工处理,现在看来太麻烦.
能不能像@JsonFormat(pattern = "yyyy-MM-dd")格式化日期类型一样,去配置BigDecimal序列化呢?
于是开始研究jackson序列化源码,很快找到了方案.
注:因为spring boot 默认使用jackson序列化,本文基于jackson方案,后续研究基于fastjson序列化方案
往常都是手工格式化,如下
public class BigDecimalDemoVo {
private String id;
private BigDecimal avgScore;
}
BigDecimalDemoVo b = new BigDecimalDemoVo()
b.setAvgScore(new BigDecimal("1.235556").setScale(2, BigDecimal.ROUND_HALF_UP))
自定义通用序列化器后
- 使用方式
@Data
@Accessors(chain = true)
public class BigDecimalDemoVo {
@JsonFormat(shape = Shape.STRING)
private Integer id;
//原类型
private BigDecimal avgScore;
// 精确二位小数,四舍五入
@JsonSerialize(using = SerializerBigDecimal.class)
private BigDecimal avgScore1;
// 保留两位整数和三位小数,四舍五入
@JsonFormat(pattern = "00.000", shape = Shape.STRING)
@JsonSerialize(using = SerializerBigDecimal.class)
private BigDecimal avgScore2;
// 化为百分比且精确两位小数,四舍五入
@JsonFormat(pattern = "#.##%", shape = Shape.STRING)
@JsonSerialize(using = SerializerBigDecimal.class)
private BigDecimal avgScore3;
}
@JsonFormat说明:
pattern = "00.000" 控制小数点前后位数,更多格式化表达式,请参考jdk的DecimalFormat类型
shape = Shape.STRING 序列化后的目标类型,像%给到前端必须得是字符串类型.
- 接口返回效果
自定义序列化器实现源码
更多json序列化自定义可以参考框架相关类
com.fasterxml.jackson.databind.ser.std.NumberSerializer
com.fasterxml.jackson.databind.ser.std.EnumSerializer
/**
* BigDecimal序列化(默认保留二位小数和四舍五入)
* @author hushowly
* @date 2020年7月15日 下午12:58:58
*/
public class SerializerBigDecimal extends JsonSerializer<BigDecimal> implements ContextualSerializer{
protected DecimalFormat decimalFormat;
public SerializerBigDecimal() {
}
public SerializerBigDecimal(DecimalFormat decimalFormat) {
this.decimalFormat = decimalFormat;
}
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if(Objects.isNull(value)) {
gen.writeNull();
} else {
if(null != decimalFormat) {
gen.writeNumber(decimalFormat.format(value));
}else {
gen.writeNumber(value.setScale(2, BigDecimal.ROUND_HALF_UP));
}
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException {
JsonFormat.Value format = findFormatOverrides(prov, property, handledType());
if (format == null) {
return this;
}
if (format.hasPattern()) {
DecimalFormat decimalFormat = new DecimalFormat(format.getPattern());
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
return new SerializerBigDecimal(decimalFormat);
}
return this;
}
protected JsonFormat.Value findFormatOverrides(SerializerProvider provider,
BeanProperty prop, Class<?> typeForDefaults)
{
if (prop != null) {
return prop.findPropertyFormat(provider.getConfig(), typeForDefaults);
}
return provider.getDefaultPropertyFormat(typeForDefaults);
}
}