Jackson自定义序列化注解之 保留小数位数 (默认2位小数 四舍五入)

本文介绍了如何在Jackson库中使用自定义注解DecimalPrecision来控制字段的小数位数和舍入模式,以及DecimalPrecisionHandler类的具体实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. 注解类

2. 注解处理类DecimalPrecisionHandler


        此文章需要先看Jackson自定义序列化注解 (支持同时使用多个自定义序列化注解)

  1. 注解类

    import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.math.RoundingMode;
    
    /**
     * 保留小数位数 (默认2位小数 四舍五入)
     *
     * @author ds
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @JacksonAnnotationsInside
    @JacksonBaseSerialize(handlerClazz = DecimalPrecisionHandler.class)
    public @interface DecimalPrecision {
    
        /**
         * 保留小数位数 (默认2位小数)
         */
        int precision() default 2;
    
        /**
         * 舍入模式 (默认四舍五入)
         */
        RoundingMode roundingMode() default RoundingMode.HALF_UP;
    
        /**
         * 是否单独返回 (默认否)
         */
        boolean isSeparately() default false;
    
        /**
         * 是否去除末尾的0 (默认是)
         */
        boolean isStripTrailingZeros() default true;
    }
    
  2. 注解处理类DecimalPrecisionHandler

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.BeanProperty;
    import com.fasterxml.jackson.databind.SerializerProvider;
    
    import java.io.IOException;
    import java.math.BigDecimal;
    import java.math.BigInteger;
    import java.math.RoundingMode;
    
    /**
     * Jackson 序列化处理类
     * 保留小数位数 (默认2位小数 四舍五入)
     *
     * @author ds
     */
    public class DecimalPrecisionHandler implements IJacksonBaseSerializeHandler<DecimalPrecision, Object> {
    
        /**
         * Method that can be called to ask implementation to serialize
         * values of type this serializer handles.
         *
         * @param value       Value to serialize; can <b>not</b> be null.
         * @param gen         Generator used to output resulting Json content
         * @param serializers Provider that can be used to get serializers for
         *                    serializing Objects value contains, if any.
         */
        @Override
        public void serializeHandler(BeanProperty beanProperty, DecimalPrecision annotation, Object value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
    
            // 为空直接跳过
            if (beanProperty == null) {
                return;
            }
            BigDecimal temp = this.handlerBigDecimal(value);
    
            int precision = annotation.precision();
            RoundingMode roundingMode = annotation.roundingMode();
    
            temp = temp.setScale(precision, roundingMode);
    
            // 是否去除末尾的0 (默认是)
            if (annotation.isStripTrailingZeros()) {
                temp = temp.stripTrailingZeros();
            }
    
            String valueStr = temp.toPlainString();
            if (annotation.isSeparately()) {
                // 单独返回
                gen.writeStringField(beanProperty.getName() + "DecimalPrecision", valueStr);
            } else {
                // 当前字段中返回
                gen.writeStringField(beanProperty.getName(), valueStr);
            }
        }
    
        /**
         * 处理成BigDecimal类型
         *
         * @param value Value to serialize; can <b>not</b> be null.
         * @return 对应的BigDecimal类型实例
         */
        private BigDecimal handlerBigDecimal(Object value) {
            BigDecimal temp;
            if (value instanceof BigDecimal) {
                temp = (BigDecimal) value;
            } else if (value instanceof Integer) {
                temp = BigDecimal.valueOf((Integer) value);
            } else if (value instanceof Long) {
                temp = BigDecimal.valueOf((Long) value);
            } else if (value instanceof BigInteger) {
                temp = new BigDecimal((BigInteger) value);
            } else if (value instanceof String) {
                temp = new BigDecimal((String) value);
            } else {
                throw new NumberFormatException("No digits found.");
            }
            return temp;
        }
    }
    

<think>好的,我现在要解决用户的问题,即如何通过JsonSerializer处理BigDecimal,并根据对象中指定字段动态设置保留小数位数。用户希望得到示例代码,说明如何根据对象中的某个字段值来决定另一个BigDecimal字段的小数位保留情况。 首先,我需要回忆一下Spring框架中JsonSerializer的用法。JsonSerializer是Jackson库的一部分,用于自定义序列化过程。用户提到需要动态设置保留小数位数,这意味着在序列化时,需要根据对象中的某个字段的值来决定另一个字段的格式化方式。 接下来,我需要确定如何获取对象中的其他字段值。通常,在自定义的JsonSerializer中,可以通过context参数来访问整个对象。例如,在serialize方法中,可以通过JsonGenerator的getCurrentValue()方法获取当前正在序列化的对象实例。这样就可以访问其他字段的值了。 然后,我需要考虑如何动态地设置小数位数。假设用户有一个对象,其中包含一个BigDecimal字段和一个指示保留数的字段。例如,对象中有一个amount字段和一个decimalPlaces字段,decimalPlaces的值决定了amount应该保留小数。这时候,自定义的JsonSerializer需要读取decimalPlaces的值,并据此设置amount的格式化方式。 接下来是具体的实现步骤。首先,创建一个自定义注解,用于标记需要动态保留小数位的字段,并指定依赖的字段名。例如,定义一个@DynamicDecimalFormat注解,其中包含sourceField属性,用于指定存储小数位数的字段名。 然后,实现一个JsonSerializer的子类,例如DynamicBigDecimalSerializer。在serialize方法中,需要获取当前对象,通过反射读取sourceField的值,即小数位数,然后对BigDecimal值进行四舍五入处理。需要注意的是,处理过程中要考虑BigDecimal的不可变性,使用setScale方法并指定舍入模式。 另外,需要将这个自定义的Serializer注册到Jackson的模块中,并在对应的字段上使用@JsonSerialize注解指定使用这个Serializer。同时,可能需要处理异常情况,例如sourceField不存在或不是整数类型,这时候可以记录错误并返回原始值。 可能遇到的问题包括:如何通过反射获取私有字段的值,需要设置字段的可访问性;处理可能的异常情况,如字段不存在或类型错误;以及确保线程安全,因为JsonSerializer可能被多个线程使用。不过,由于每次序列化都是针对特定对象实例,这里应该不会有线程安全问题。 最后,验证示例代码的正确性。例如,创建一个TestBean类,其中包含amount和decimalPlaces字段,在amount上使用@DynamicDecimalFormat注解并指定sourceField为decimalPlaces。当decimalPlaces为2时,amount应该被格式化为两小数;当decimalPlaces为3时,则保留小数。测试序列化后的JSON是否符合预期。 总结来说,实现的关键点包括自定义注解、通过反射获取依赖字段的值、动态设置小数位数,并将这些整合到JsonSerializer中。同时,要考虑异常处理和代码的健壮性。</think>### 通过 `JsonSerializer` 动态设置 `BigDecimal` 小数位数实现 以下是一个完整的示例代码,通过自定义注解和 `JsonSerializer` 实现 **根据对象字段值动态控制 `BigDecimal` 小数位数**,例如:根据对象中 `decimalPlaces` 字段的值决定 `amount` 字段的保留数。 --- #### 1. 定义动态小数位数注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @JacksonAnnotationsInside @JsonSerialize(using = DynamicBigDecimalSerializer.class) public @interface DynamicDecimalFormat { // 指定用于动态获取小数位数的字段名称 String sourceField(); } ``` --- #### 2. 实现 `JsonSerializer` 动态逻辑 ```java public class DynamicBigDecimalSerializer extends JsonSerializer<BigDecimal> { @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider) throws IOException { // 获取当前序列化的对象 Object currentObj = gen.getCurrentValue(); Class<?> objClass = currentObj.getClass(); // 获取注解配置的源字段名称 DynamicDecimalFormat annotation = gen.getContextAnnotation(DynamicDecimalFormat.class); String sourceFieldName = annotation.sourceField(); try { // 反射获取源字段值(小数位数) Field sourceField = objClass.getDeclaredField(sourceFieldName); sourceField.setAccessible(true); int decimalPlaces = (int) sourceField.get(currentObj); // 动态设置小数位序列化 BigDecimal roundedValue = value.setScale(decimalPlaces, RoundingMode.HALF_UP); gen.writeNumber(roundedValue); } catch (Exception e) { // 异常处理:直接返回原始值 gen.writeNumber(value); provider.reportMappingProblem("Failed to resolve dynamic decimal places: " + e.getMessage()); } } } ``` --- #### 3. 在实体类中使用注解 ```java public class Order { @DynamicDecimalFormat(sourceField = "decimalPlaces") private BigDecimal amount; private int decimalPlaces; // 动态控制小数位数(例如:2表示保留) // Getter & Setter } ``` --- #### 4. 测试序列化结果 ```java Order order = new Order(); order.setAmount(new BigDecimal("123.45678")); order.setDecimalPlaces(2); // 动态设置为保留2 // 序列化输出:{"amount":123.46, "decimalPlaces":2} String json = new ObjectMapper().writeValueAsString(order); ``` --- ### 关键点说明 1. **动态依赖字段**:通过注解的 `sourceField` 指定控制小数位数的字段名,支持灵活配置[^4]。 2. **反射机制**:利用反射动态获取对象字段值,需处理字段访问权限异常[^3]。 3. **异常容错**:若配置字段不存在或类型错误,回退到原始值序列化[^2]。 4. **线程安全**:`JsonSerializer` 实例由 Jackson 管理,反射操作在单次序列化中完成,无线程安全问题。 --- ### 扩展优化 - **缓存反射字段**:可通过 `ConcurrentHashMap` 缓存 `Field` 对象提升性能。 - **支持默认值**:在注解中添加 `defaultScale` 属性,当源字段为 `null` 时使用默认值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值