概述
在开发中有时候会遇到一些内容返回时需要翻译,或者一些内容在序列化之前需要特殊处理(脱敏啥的)。
一般对单个属性可以直接用jackson
的序列化注解对某个属性单独处理com.fasterxml.jackson.databind.annotation.JsonSerialize(using= xxx.class)
但是直接使用不太灵活,可以进一步引入注解,配上参数来灵活的序列化。
这里就会涉及到com.fasterxml.jackson.databind.ser.ContextualSerializer
它可以通过回调拿到序列化的属性上的注解参数。
我的jdk
版本是17
springboot
是3.1.2
因为用了spring-web
,默认用的就是jackson
,所以不需要再额外引入jackson
依赖了
自定义注解序列化化
通过com.fasterxml.jackson.databind.ser.ContextualSerializer
序列化时获取字段上的注解信息;ContextualSerializer.createContextual
方法只会在第一次序列化字段时调用(因为字段的上下文信息在运行期不会改变),所以不用担心影响性能。
类似JsonFormat
注解也是这样使用的文章来源:https://www.toymoban.com/news/detail-731859.html
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对应)
* <p>
* 默认取当前字段的值 如果设置了 @{@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);
}
}
BeanSerializerModifier自定义null序列化
package com.xxx.xxx.translation.core.handler;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
/**
* Bean 序列化修改器 自定义 Null 值修改
*/
public class TranslationBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
//这里做个业务判断,和上面的序列化对象TranslationHandler对应
if (writer.getSerializer() instanceof TranslationHandler) {
//简单来说就是给当前属性声明一个null值的序列化处理对象
writer.assignNullSerializer(writer.getSerializer());
}
}
return beanProperties;
}
}
注册null值序列化文章来源地址https://www.toymoban.com/news/detail-731859.html
package com.xxx.common.translation.config;
import com.xxx.common.translation.core.handler.TranslationBeanSerializerModifier;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Slf4j
@Configuration
public class TranslationConfig {
@Resource
private ObjectMapper objectMapper;
@PostConstruct
public void init() {
// 设置 Bean 序列化修改器
objectMapper.setSerializerFactory(
objectMapper.getSerializerFactory()
.withSerializerModifier(new TranslationBeanSerializerModifier()));
}
}
到了这里,关于springboot对象序列化自定义序列化注解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!