springboot 三种修改接口序列化返回值的方法
·
背景
公司项目需要实现多语言,需要通过序列化返回值获取多语言的值,此处通过三种方式实现。
创建自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD) // 仅允许标记在字段上
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,供反射读取
@JacksonAnnotationsInside // 告诉Jackson这是一个组合注解,内部注解生效
@JsonSerialize(using = I18nValueSerializer.class) // 指定使用的序列化器
public @interface I18nValue {
// 需要拼接的源字段名称
String sourceFieldName();
// 需要拼接的类型标识
String baseCodeType();
}
创建自定义序列化器
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 com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.lang.reflect.Field;
public class I18nValueSerializer extends StdSerializer<Object> implements ContextualSerializer {
private String targetFieldName; // 要获取值的字段名
private String type; // 要拼接的类型
// 默认构造函数
public I18nValueSerializer() {
super(Object.class);
}
// 带参数的构造函数
public I18nValueSerializer(String targetFieldName, String type) {
super(Object.class);
this.targetFieldName = targetFieldName;
this.type = type;
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
// 这里value是当前字段的值,但我们需要的是targetFieldName字段的值
// 所以需要获取父对象来反射获取目标字段的值
try {
// 获取目标字段的值
String targetValue = getTargetFieldValue(gen.getCurrentValue());
String finalValue = targetValue + (type != null && !type.isEmpty() ? "_" + type : "");
gen.writeString(finalValue);
} catch (Exception e) {
// 如果出错,写入默认值或错误信息
gen.writeString("error_" + type);
}
}
/**
* 通过反射获取目标字段的值
*/
private String getTargetFieldValue(Object currentObject) throws Exception {
if (currentObject == null || targetFieldName == null) {
return "";
}
Class<?> clazz = currentObject.getClass();
Field targetField = null;
// 遍历所有字段查找目标字段
for (Field field : clazz.getDeclaredFields()) {
if (targetFieldName.equals(field.getName())) {
targetField = field;
break;
}
}
if (targetField == null) {
throw new NoSuchFieldException("Field not found: " + targetFieldName);
}
targetField.setAccessible(true);
Object fieldValue = targetField.get(currentObject);
return fieldValue != null ? fieldValue.toString() : "";
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
throws JsonMappingException {
if (property == null) {
return this;
}
// 获取字段上的I18nValue注解
I18nValue i18nValue = property.getAnnotation(I18nValue.class);
if (i18nValue == null) {
i18nValue = property.getContextAnnotation(I18nValue.class);
}
if (i18nValue != null) {
return new I18nValueSerializer(i18nValue.sourceFieldName(), i18nValue.baseCodeType());
}
return this;
}
}
创建对象级别的序列化器(替代方案)
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.lang.reflect.Field;
public class I18nObjectSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object object, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeStartObject();
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
I18nValue i18nValue = field.getAnnotation(I18nValue.class);
try {
if (i18nValue != null) {
// 获取目标字段的值
String targetValue = getFieldValue(object, i18nValue.sourceFieldName());
String finalValue = targetValue +
(i18nValue.baseCodeType() != null && !i18nValue.baseCodeType().isEmpty() ?
"_" + i18nValue.baseCodeType() : "");
gen.writeStringField(field.getName(), finalValue);
} else {
// 正常序列化其他字段
Object fieldValue = field.get(object);
provider.defaultSerializeField(field.getName(), fieldValue, gen);
}
} catch (Exception e) {
gen.writeStringField(field.getName(), "error");
}
}
gen.writeEndObject();
}
private String getFieldValue(Object object, String fieldName) throws Exception {
Field targetField = object.getClass().getDeclaredField(fieldName);
targetField.setAccessible(true);
Object value = targetField.get(object);
return value != null ? value.toString() : "";
}
}
实体类使用示例
此处可以使用@Data注解,减少代码量
// 方式一:在字段上使用注解和序列化器
public class UserDTO {
private Long id;
private String username;
private Integer status;
// 这个字段会显示status字段的值 + "_text"
@I18nValue(fieldName = "status", type = "text")
@JsonSerialize(using = I18nValueSerializer.class)
private String statusText;
// 这个字段会显示username字段的值 + "_name"
@I18nValue(fieldName = "username", type = "name")
@JsonSerialize(using = I18nValueSerializer.class)
private String userNameDisplay;
// 正常字段
private String email;
// getter和setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getStatus() { return status; }
public void setStatus(Integer status) { this.status = status; }
public String getStatusText() { return statusText; }
public void setStatusText(String statusText) { this.statusText = statusText; }
public String getUserNameDisplay() { return userNameDisplay; }
public void setUserNameDisplay(String userNameDisplay) { this.userNameDisplay = userNameDisplay; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
// 方式二:在整个类上使用序列化器
@JsonSerialize(using = I18nObjectSerializer.class)
public class ProductDTO {
private String productCode;
private String productName;
private Double price;
@I18nValue(fieldName = "productCode", type = "code")
private String displayCode;
@I18nValue(fieldName = "productName", type = "name")
private String displayName;
// getter和setter...
}
控制器示例
@RestController
public class DemoController {
@GetMapping("/user")
public UserDTO getUser() {
UserDTO user = new UserDTO();
user.setId(1L);
user.setUsername("zhangsan");
user.setStatus(1);
user.setEmail("zhangsan@example.com");
// statusText和userNameDisplay字段会自动序列化
return user;
}
@GetMapping("/product")
public ProductDTO getProduct() {
ProductDTO product = new ProductDTO();
product.setProductCode("P001");
product.setProductName("iPhone");
product.setPrice(5999.0);
return product;
}
}
预期的JSON输出
UserDTO输出:
{
"id": 1,
"username": "zhangsan",
"status": 1,
"statusText": "1_text",
"userNameDisplay": "zhangsan_name",
"email": "zhangsan@example.com"
}
ProductDTO输出:
{
"displayCode": "P001_code",
"displayName": "iPhone_name",
"productCode": "P001",
"productName": "iPhone",
"price": 5999.0
}
高级版本:支持方法获取值
需要将I18nValueSerializer 的targetFieldName和type和getTargetFieldValue改为public
public class EnhancedI18nValueSerializer extends I18nValueSerializer {
@Override
private String getTargetFieldValue(Object currentObject) throws Exception {
if (currentObject == null || targetFieldName == null) {
return "";
}
Class<?> clazz = currentObject.getClass();
// 先尝试通过字段获取
try {
Field targetField = clazz.getDeclaredField(targetFieldName);
targetField.setAccessible(true);
Object fieldValue = targetField.get(currentObject);
return fieldValue != null ? fieldValue.toString() : "";
} catch (NoSuchFieldException e) {
// 如果字段不存在,尝试通过getter方法获取
String getterName = "get" + targetFieldName.substring(0, 1).toUpperCase() +
targetFieldName.substring(1);
try {
java.lang.reflect.Method getter = clazz.getMethod(getterName);
Object result = getter.invoke(currentObject);
return result != null ? result.toString() : "";
} catch (NoSuchMethodException ex) {
throw new NoSuchFieldException("Field or getter not found: " + targetFieldName);
}
}
}
}
配置全局序列化器(可选)
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
// 注册注解查找器
module.setSerializerModifier(new BeanSerializerModifier() {
@Override
public List<BeanPropertyWriter> changeProperties(
SerializationConfig config,
BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
I18nValue i18nValue = writer.getAnnotation(I18nValue.class);
if (i18nValue != null) {
writer.assignSerializer(
new I18nValueSerializer(i18nValue.fieldName(), i18nValue.type())
);
}
}
return beanProperties;
}
});
mapper.registerModule(module);
return mapper;
}
}
总结
个人实践的I18nObjectSerializer 可以,I18nValueSerializer 不行,具体原因还在排查中(I18nValueSerializer失效的原因是序列号的字段,比如displayName是null的,序列号的时候会忽略null值,所以不生效,建议使用I18nObjectSerializer )
更多推荐
所有评论(0)