I have a JSON
seriliazer which serializes a BigDecimal
in the way presented in this SO answer :
public class MoneySerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal bigDecimal, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(bigDecimal.toPlainString());
}
}
It is used in my POJO
:
public class MoneyBean {
//...
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amount1;
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amount2;
// getters/setters
}
Requirements:
Is there any way the MoneySerializer
can be adjusted to pass another argument as minimumScale
so that it can remove automatically? Obviously this logic can be moved to service or we can create different serializer for each case but here I am looking for a single annotation which solves the problem something like:
@MyJsonSerialize(using = MoneySerializer.class, minimumScale = 2)
private BigDecimal amount1; //100.00000 -> 100.00
@MyJsonSerialize(using = MoneySerializer.class, minimumScale = 0)
private BigDecimal amount2; //100.00000 -> 100
@MyJsonSerialize(using = MoneySerializer.class, minimumScale = 4)
private BigDecimal amount3; //100.00000 -> 100.0000
Tried with different serializer or moving trailing zeros removal logic to service class and that is working but looking for a configurable annotation based solution.
I did some research and thought about the code for you before I went to bed. Try what I wrote :)
add this annotation class
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAccuracy {
int value() default 0;
}
and
public class Entity {
@MyAccuracy(2)
@JsonSerialize(using = MoneySerializer.class)
public BigDecimal amount1;
@MyAccuracy(0)
@JsonSerialize(using = MoneySerializer.class)
public BigDecimal amount2;
@MyAccuracy(4)
@JsonSerialize(using = MoneySerializer.class)
public BigDecimal amount3;
}
and
public class MoneySerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal bigDecimal, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String fieldName = jsonGenerator.getOutputContext().getCurrentName();
Class css = jsonGenerator.getOutputContext().getCurrentValue().getClass();
boolean edit = false;
try{
MyAccuracy myAccuracy = css.getField(fieldName).getAnnotation(MyAccuracy.class);
jsonGenerator.writeString(bigDecimal.setScale(myAccuracy.value(),BigDecimal.ROUND_HALF_UP).toPlainString());
edit = true;
}catch (Exception e){
e.printStackTrace();
}
if(!edit) {
jsonGenerator.writeString(bigDecimal.toPlainString());
}
}
}
finally
public static void main(String[] args) throws JsonProcessingException {
Entity entity = new Entity();
entity.amount1 = BigDecimal.valueOf(2.2222);
entity.amount2 = BigDecimal.valueOf(3.3333);
entity.amount3 = BigDecimal.valueOf(4.4444);
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(entity);
System.out.println(jsonString);
}
final result
{"amount1":"2.22","amount2":"3","amount3":"4.4444"}
Good luck~
When we want to customise serialiser per field we need to use com.fasterxml.jackson.databind.ser.ContextualSerializer
to create customised version. It allows us to read annotation for a given field and we do that only once so during the serialisation we do not need to execute Reflection API
. Also, we can reuse com.fasterxml.jackson.annotation.JsonFormat
annotation to provide pattern for BigDecimal
. Using JsonFormat
we can also handle shape in case we want to print it as a number for some properties. Below you can find complete example:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import lombok.Data;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Objects;
public class BigDecimalApp {
public static void main(String[] args) throws IOException {
var mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.build();
var entity = new Entity();
entity.setAmount1(new BigDecimal("100.4567"));
entity.setAmount2(new BigDecimal("100.4567"));
entity.setAmount3(new BigDecimal("100.4567"));
entity.setAmountAsNumber1(new BigDecimal("100.4567"));
entity.setAmountAsNumber2(new BigDecimal("100.4567"));
entity.setAmountAsNumber3(new BigDecimal("100.4567"));
mapper.writeValue(System.out, entity);
}
}
@Data
class Entity {
@JsonFormat(pattern = "#")
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amount1;
@JsonFormat(pattern = "#.00")
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amount2;
@JsonFormat(pattern = "#.0000")
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amount3;
@JsonFormat(shape = Shape.NUMBER_INT)
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amountAsNumber1;
@JsonFormat(pattern = "#.00", shape = Shape.NUMBER)
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amountAsNumber2;
@JsonFormat(pattern = "#.0000", shape = Shape.NUMBER)
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amountAsNumber3;
}
class MoneySerializer extends StdSerializer<BigDecimal> implements ContextualSerializer {
private static final MoneySerializer INSTANCE = new MoneySerializer();
private final NumberFormat formatter;
private final Shape shape;
public MoneySerializer() {
this(DecimalFormat.getInstance(), Shape.STRING);
}
protected MoneySerializer(NumberFormat formatter, Shape shape) {
super(BigDecimal.class);
this.formatter = Objects.requireNonNull(formatter);
this.shape = Objects.requireNonNull(shape);
}
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (Objects.isNull(value)) {
jsonGenerator.writeNull();
} else {
switch (shape) {
case NUMBER, NUMBER_FLOAT, NATURAL -> jsonGenerator.writeNumber(value.setScale(formatter.getMaximumFractionDigits(), RoundingMode.HALF_UP));
case NUMBER_INT -> jsonGenerator.writeNumber(value.intValue());
default -> jsonGenerator.writeString(formatter.format(value));
}
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
JsonFormat jsonFormat = property.getAnnotation(JsonFormat.class);
if (Objects.isNull(jsonFormat)) {
return INSTANCE;
}
return new MoneySerializer(new DecimalFormat(jsonFormat.pattern()), jsonFormat.shape());
}
}
Above code prints:
{
"amount1" : "100",
"amount2" : "100.46",
"amount3" : "100.4567",
"amountAsNumber1" : 100,
"amountAsNumber2" : 100.46,
"amountAsNumber3" : 100.4567
}
See also:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.