springboot对象序列化自定义序列化注解

概述

在开发中有时候会遇到一些内容返回时需要翻译,或者一些内容在序列化之前需要特殊处理(脱敏啥的)。

一般对单个属性可以直接用jackson的序列化注解对某个属性单独处理
com.fasterxml.jackson.databind.annotation.JsonSerialize(using= xxx.class)

但是直接使用不太灵活,可以进一步引入注解,配上参数来灵活的序列化。

这里就会涉及到
com.fasterxml.jackson.databind.ser.ContextualSerializer
它可以通过回调拿到序列化的属性上的注解参数。

我的jdk版本是17
springboot3.1.2
因为用了spring-web,默认用的就是jackson,所以不需要再额外引入jackson依赖了

上demo

package org.xxx.common.translation.annotation;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.qps.common.translation.core.handler.TranslationHandler;

import java.lang.annotation.*;

/**
 * 通用翻译注解
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
//让jackson的注解拦截器(com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector)能发现当前注解
@JacksonAnnotationsInside
//指定当前注解修饰的属性/方法使用具体哪个序列化类来序列化
@JsonSerialize(using = TranslationHandler.class)
public @interface Translation {

    /**
     * 类型 (需与实现类上的 {@link TranslationType} 注解type对应)
     * 

* 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值 */ String type(); /** * 映射字段 (如果不为空则取此字段的值) */ String mapper() default ""; /** * 其他条件 例如: 字典type(sys_user_sex) */ String other() default ""; } package org.xxx.xxx.translation.core.handler; import cn.hutool.core.util.ObjectUtil; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import org.xxx.common.core.utils.StringUtils; import org.xxx.common.core.utils.reflect.ReflectUtils; import org.xxx.common.translation.annotation.Translation; import org.xxx.common.translation.core.TranslationInterface; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; /** * 翻译处理器 * 通过回调(ContextualSerializer.createContextual),将声明的属性上的Translation注解,传递给当前的序列化器 */ @Slf4j public class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer { /** * 全局翻译实现类映射器 */ public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>(); private Translation translation; @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { //这里是通过一个接口,提前定义好了一批翻译类,这里通过注解上的type去获取对应的翻译类 TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type()); if (ObjectUtil.isNotNull(trans)) { // 如果映射字段不为空 则取映射字段的值 if (StringUtils.isNotBlank(translation.mapper())) { //通过反射去调用注解中指定的属性(比如shopName通过shopId去翻译) value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper()); } // 如果为 null 直接写出 if (ObjectUtil.isNull(value)) { gen.writeNull(); return; } //调用翻译类进行翻译 Object result = trans.translation(value, translation.other()); gen.writeObject(result); } else { gen.writeObject(value); } } //通过回调,在第一次调用的时候获取注解参数,并赋值给当前class中的注解变量 @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { Translation translation = property.getAnnotation(Translation.class); if (Objects.nonNull(translation)) { this.translation = translation; return this; } return prov.findValueSerializer(property.getType(), property); } }

你可能感兴趣的:(springboot,JAVA,spring,boot,java,spring)