Java接口响应序列化时使用自定义注解特殊处理数据

在接口响应时,有很多情况需要做数据转换,比如数据脱敏,null值给默认,数据类型转换...,那怎么避免在代码里写过多的 if 判断和处理逻辑呢?

Java 接口响应数据都是使用jackson作为序列化框架的,其中有一个对象

NopAnnotationIntrospector:字面意思 “注释内省器”

在jackson自定义builder的时候可以添加进去

@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
	return builder -> builder
			.annotationIntrospector(new ResultDataAnnotationIntrospector()) // 自定义内省器(只能加一个,后边加的会覆盖前边的)
			.serializerByType(Long.class, new NumberToStringSerializer()) // 大数字转成字符串,避免前端js接口丢失精度
			.serializerByType(BigDecimal.class, new NumberToStringSerializer()) // 浮点类型转成字符串,避免前端js接口丢失精度,统一小数位数
			.serializerByType(LocalDateTime.class, localDateTimeserializer()) // 日期格式化输出
			.serializerByType(Boolean.class, new BooleanToNumberSerializer()) // Boolean类型输出为数字
			.serializerByType(IEnum.class, new EnumToNumberSerializer()) // 把枚举类输出为自定义的数据
			.serializationInclusion(JsonInclude.Include.NON_NULL) // 自动去掉null值
			;
}
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.util.EnumResolver;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

// 内省器
@Slf4j
public class ResultDataAnnotationIntrospector extends NopAnnotationIntrospector {

    // 自定义的多个处理不同注解的内省器
    private final List<AbstractResultValueIntrospector> introspectorList = new ArrayList<>();

    // 空值时的处理
    private final NullValueAnnotationIntrospector nullIntrospector = new NullValueAnnotationIntrospector();

    // 循环处理一个属性上的多个注解
    // 执行顺序是 按照List的添加先后排列顺序执行序列化
    public ResultDataAnnotationIntrospector() { 
        super();
        // introspectorList.add(new NullValueAnnotationIntrospector());
        introspectorList.add(new MoneyAnnotationIntrospector());
        introspectorList.add(new MaskingAnnotationIntrospector());
    }

    // “反序列化”时的处理
    // 如果参数接收对象属性是枚举,但是传参是数字,jackson默认是枚举的下标数字,
    // 但是系统里有自定义的Code值,所以转换成自己想要的数字匹配
    @Override
    public Object findDeserializer(Annotated am) {
        if (am instanceof AnnotatedMethod) {
            AnnotatedMethod annotatedMethod = (AnnotatedMethod) am;
            if (annotatedMethod.getParameterCount() == 1) {
                AnnotatedParameter parameter = annotatedMethod.getParameter(0);
                JavaType type = parameter.getType();
                if (type.isEnumType()) {
                    EnumResolver enumResolver = EnumResolver.constructUnsafe(type.getRawClass(), this);
                    return new IntegerToEnumDeserializer(enumResolver, false);
                }
            }
        }
        return super.findDeserializer(am);
    }

    // 序列化“非空值”时的处理
    @Override
    public Object findSerializer(Annotated am) {
        Object serializer = null;
        for (AbstractResultValueIntrospector introspector : introspectorList) {
            Object newSerializer = introspector.findSerializer(am, (AbstractResultSerializer) serializer);
            if (newSerializer != null) {
                serializer = newSerializer;
            }
        }
        return serializer;
    }

    // 序列化“空值”时的处理
    @Override
    public Object findNullSerializer(Annotated am) {
        // 首先处理空值
        Object serializer = nullIntrospector.findNullSerializer(am, null);
        for (AbstractResultValueIntrospector introspector : introspectorList) {
            // 处理完空值之后,后续的就不再是对空值的序列化了,所以使用“findSerializer”
            Object newSerializer = introspector.findSerializer(am, (AbstractResultSerializer) serializer);
            if (newSerializer != null) {
                serializer = newSerializer;
            }
        }
        return serializer;
    }

}
import com.fasterxml.jackson.databind.introspect.Annotated;
import lombok.Getter;

// 抽象内省器
@Getter
public abstract class AbstractResultValueIntrospector {

    public Object findSerializer(Annotated am, AbstractResultSerializer serializer) {
        return null;
    }

    public Object findNullSerializer(Annotated am, AbstractResultSerializer serializer) {
        return null;
    }

}
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
// 抽象结果序列化处理器
public abstract class AbstractResultSerializer<T> extends StdScalarSerializer<T> {

    // 属性上多个注解,链式处理序列化
    protected final AbstractResultSerializer preSerializer;

    protected AbstractResultSerializer(Class<T> t, AbstractResultSerializer preSerializer) {
        super(t);
        this.preSerializer = preSerializer;
    }

    protected AbstractResultSerializer(Class<?> t, boolean dummy, AbstractResultSerializer preSerializer) {
        super(t, dummy);
        this.preSerializer = preSerializer;
    }

    // 抽象方法,只处理值
    protected abstract T serializeValue(T value);

}

示例:空值处理

// 处理空值序列化
@Slf4j
public class NullValueAnnotationIntrospector extends AbstractResultValueIntrospector {

    @Override
    public Object findNullSerializer(Annotated am, AbstractResultSerializer serializer) {
        BooleanNullValue nullValue = am.getAnnotation(BooleanNullValue.class);
        if (nullValue != null) {
            return new NullValueSerializer<>(Boolean.class, nullValue.value(), nullValue.defaultNull(), serializer);
        }
        StringNullValue stringNullValue = am.getAnnotation(StringNullValue.class);
        if (stringNullValue != null) {
            return new NullValueSerializer<>(String.class, stringNullValue.value(), stringNullValue.defaultNull(), serializer);
        }
        NumberNullValue numberNullValue = am.getAnnotation(NumberNullValue.class);
        if (numberNullValue != null) {
            boolean defaultNull = numberNullValue.value().length == 0;
            Long defaultValue = defaultNull ? null : numberNullValue.value()[0];
            return new NullValueSerializer<>(Number.class, defaultValue, defaultNull, serializer);
        }
        return null;
    }

}
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class NullValueSerializer<T> extends AbstractResultSerializer<T> {

    private final T defaultValue;

    private final Boolean defaultNull;

    public NullValueSerializer(Class<T> tClass, T defaultValue, boolean defaultNull, AbstractResultSerializer serializer) {
        super(tClass, serializer);
        this.defaultValue = defaultValue;
        this.defaultNull = defaultNull;
    }

    @Override
    public T serializeValue(T value) {
        if (ObjectUtil.isEmpty(value)) {
            return this.defaultNull ? null : this.defaultValue;
        }
        return value;
    }

    @Override
    public void serialize(T value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
        if (preSerializer != null) {
            value = (T) preSerializer.serializeValue(value);
        }
        value = serializeValue(value);
        gen.writeObject(value);
    }

}

处理数据也可以使用切面做,但是有几个缺点:

  • 数据处理后,类型必须要跟对象的属性类型一样
  • 如果数据有多层,就需要自己写循环迭代处理,比较麻烦

那么使用jackson内省器,在组装json的时候处理想要的数据,就会避免使用切面时遇到的问题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值