Jackson将带自定义注解的字段转为含注解信息的JSON对象方案咨询
优雅实现带自定义注解字段的JSON序列化方案
刚好之前做过类似的JSON序列化定制需求,给你两个比手动写三元组工具类优雅得多的方案,都是基于主流JSON库的自定义扩展,配置一次就能全局生效,不用重复写转换逻辑:
方案一:基于Jackson自定义序列化器(Spring生态首选)
Jackson是Spring Boot默认的JSON处理库,通过自定义JsonSerializer可以精准控制带@Descriptors注解字段的序列化逻辑:
1. 确保自定义注解的元数据可被反射获取
先确认你的@Descriptors注解已经配置了正确的保留策略:
@Retention(RetentionPolicy.RUNTIME) // 必须是RUNTIME才能在运行时通过反射读取 @Target(ElementType.FIELD) public @interface Descriptors { String type(); String dbColumn(); }
2. 实现自定义序列化器
这个序列化器会自动识别字段上的@Descriptors注解,生成你想要的嵌套JSON结构:
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 DescriptorFieldSerializer extends JsonSerializer<Object> { @Override public void serialize(Object fieldValue, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 通过Jackson的上下文获取当前序列化的字段 Field currentField = null; try { currentField = serializers.findPropertyValue( gen.getCurrentValue(), gen.getOutputContext().getCurrentName() ).getMember().getField(); } catch (NoSuchFieldException e) { // 兜底处理,按默认方式序列化 gen.writeObject(fieldValue); return; } // 检查字段是否带有目标注解 if (currentField != null && currentField.isAnnotationPresent(Descriptors.class)) { Descriptors descriptors = currentField.getAnnotation(Descriptors.class); // 生成嵌套JSON结构 gen.writeStartObject(); gen.writeStringField("type", descriptors.type()); gen.writeStringField("dbColumn", descriptors.dbColumn()); gen.writeStringField("value", fieldValue != null ? fieldValue.toString() : null); gen.writeEndObject(); } else { // 无注解字段按默认逻辑序列化 gen.writeObject(fieldValue); } } }
3. 注册序列化器(全局生效)
在Spring Boot中,通过配置类把序列化器注册到Jackson的ObjectMapper,这样所有带@Descriptors的字段都会自动应用这个逻辑:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonCustomConfig { @Bean public ObjectMapper customObjectMapper() { ObjectMapper mapper = new ObjectMapper(); SimpleModule customModule = new SimpleModule(); // 注册自定义序列化器 customModule.addSerializer(Object.class, new DescriptorFieldSerializer()); mapper.registerModule(customModule); return mapper; } }
如果只是个别字段需要,也可以直接在字段上添加@JsonSerialize(using = DescriptorFieldSerializer.class)注解,不需要全局配置。
方案二:基于Gson自定义TypeAdapter(非Spring场景可选)
如果你项目用的是Gson,可以通过自定义TypeAdapterFactory来实现同样的效果:
1. 实现TypeAdapterFactory
这个工厂类会遍历对象的所有字段,识别带注解的字段并生成目标JSON结构:
import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.lang.reflect.Field; public class DescriptorTypeAdapterFactory implements TypeAdapterFactory { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type); return new TypeAdapter<T>() { @Override public void write(JsonWriter out, T targetObject) throws IOException { if (targetObject == null) { out.nullValue(); return; } out.beginObject(); // 遍历对象所有字段 Field[] fields = targetObject.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 允许访问私有字段 out.name(field.getName()); if (field.isAnnotationPresent(Descriptors.class)) { Descriptors descriptors = field.getAnnotation(Descriptors.class); // 生成嵌套结构 out.beginObject(); out.name("type").value(descriptors.type()); out.name("dbColumn").value(descriptors.dbColumn()); try { Object fieldValue = field.get(targetObject); out.name("value").value(fieldValue != null ? fieldValue.toString() : null); } catch (IllegalAccessException e) { e.printStackTrace(); } out.endObject(); } else { // 无注解字段用默认适配器序列化 try { delegateAdapter.write(out, (T) field.get(targetObject)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } out.endObject(); } @Override public T read(JsonReader in) throws IOException { // 如果需要反序列化,可以在这里实现逻辑,用户没提需求的话直接用默认适配器 return delegateAdapter.read(in); } }; } }
2. 注册到Gson
创建Gson实例时注册这个工厂类即可:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class GsonConfig { public static Gson getCustomGson() { return new GsonBuilder() .registerTypeAdapterFactory(new DescriptorTypeAdapterFactory()) .create(); } }
注意事项
- 反射权限:如果字段是
private的,一定要调用field.setAccessible(true),否则会抛出权限异常。 - 复杂字段值:如果字段不是
String类型(比如Integer、自定义对象),不要直接用toString(),可以改用原JSON库的序列化逻辑处理value字段,避免格式错误。 - 继承字段:如果需要处理父类的字段,要把
getDeclaredFields()换成getFields()或者递归遍历父类字段。
内容的提问来源于stack exchange,提问作者juggernaut
相关产品推荐
相关产品推荐

