You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:06:16