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

基于动态字段列表的Java对象指定字段动态序列化实现方案咨询

解决方案:基于动态字段列表序列化Java对象(Jackson/Gson实现)

针对你提出的动态字段序列化需求,我推荐使用Jackson的内置过滤器机制(结合Mixin避免修改原模型),这是性能最优且完全符合所有需求的方案;同时也提供Gson的实现作为备选。


一、Jackson实现方案(推荐)

Jackson提供了@JsonFilter和FilterProvider机制,可以动态控制序列化时的字段包含/排除。核心是用Mixin给原模型类添加过滤器注解,完全不需要修改你的ModelA/ModelB/ModelC代码。

步骤1:创建Mixin类(无需修改原模型)

Mixin是Jackson的特性,允许你在不修改目标类的情况下注入注解:

// 给ModelA绑定过滤器的Mixin
@JsonFilter("dynamicIncludeFilter")
public abstract class ModelAMixin {}

// 给ModelB绑定过滤器的Mixin
@JsonFilter("dynamicIncludeFilter")
public abstract class ModelBMixin {}

// 给ModelC绑定过滤器的Mixin
@JsonFilter("dynamicIncludeFilter")
public abstract class ModelCMixin {}

步骤2:实现动态包含过滤器

自定义过滤器,根据传入的字段列表判断哪些字段(包括嵌套对象的字段)需要保留:

import com.fasterxml.jackson.databind.ser.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import java.util.Set;

public class DynamicIncludeFilter extends SimpleBeanPropertyFilter {
    private final Set<String> includedFields;

    public DynamicIncludeFilter(Set<String> includedFields) {
        this.includedFields = includedFields;
    }

    @Override
    protected boolean include(PropertyWriter writer) {
        String fieldName = writer.getName();
        // 两种情况保留字段:
        // 1. 字段名直接在包含列表中
        // 2. 该字段是嵌套对象,且其下有子字段在包含列表中(比如fieldAC对应fieldBA/fieldBB)
        return includedFields.contains(fieldName) 
                || includedFields.stream().anyMatch(f -> f.startsWith(fieldName + "."));
    }
}

步骤3:编写序列化工具方法

配置ObjectMapper,注册Mixin并应用动态过滤器:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class JacksonSerializerUtil {
    public static String serializeModelA(ModelA modelA, List<String> includeFields) {
        ObjectMapper mapper = new ObjectMapper();
        
        // 注册Mixin,让原模型类拥有JsonFilter注解
        mapper.addMixIn(ModelA.class, ModelAMixin.class);
        mapper.addMixIn(ModelB.class, ModelBMixin.class);
        mapper.addMixIn(ModelC.class, ModelCMixin.class);

        Set<String> includedSet = new HashSet<>(includeFields);
        DynamicIncludeFilter filter = new DynamicIncludeFilter(includedSet);
        FilterProvider filterProvider = new SimpleFilterProvider()
                .addFilter("dynamicIncludeFilter", filter);

        try {
            return mapper.writer(filterProvider).writeValueAsString(modelA);
        } catch (Exception e) {
            throw new RuntimeException("序列化失败", e);
        }
    }
}

使用示例

public static void main(String[] args) {
    // 构造测试数据
    ModelB modelB = new ModelB();
    modelB.setFieldBA("Value 2");
    modelB.setFieldBB("Value 3");
    modelB.setFieldBC("Ignored Value");

    ModelC modelC = new ModelC();
    modelC.setFieldCA("Ignored Value");
    modelC.setFieldCB("Value 4");

    ModelA modelA = new ModelA();
    modelA.setFieldAA("Value 1");
    modelA.setFieldAB("Ignored Value");
    modelA.setFieldAC(modelB);
    modelA.setFieldAD(modelC);

    // 动态字段列表
    List<String> includeFields = List.of("fieldAA", "fieldBA", "fieldBB", "fieldCB");
    
    // 序列化
    String json = JacksonSerializerUtil.serializeModelA(modelA, includeFields);
    System.out.println(json);
}

输出结果完全符合你的期望:

{
    "fieldAA": "Value 1",
    "fieldAC": {
        "fieldBA": "Value 2",
        "fieldBB": "Value 3"
    },
    "fieldAD": {
        "fieldCB": "Value 4"
    }
}

二、Gson实现方案(备选)

如果你的项目使用Gson,可以通过自定义ExclusionStrategy和TypeAdapterFactory实现动态字段过滤:

步骤1:实现动态排除策略

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import java.util.Set;
import java.util.concurrent.ThreadLocal;

public class DynamicIncludeStrategy implements ExclusionStrategy {
    private final Set<String> includedFields;
    private final ThreadLocal<String> currentPath = ThreadLocal.withInitial(() -> "");

    public DynamicIncludeStrategy(Set<String> includedFields) {
        this.includedFields = includedFields;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        String fieldName = f.getName();
        String fullPath = currentPath.get().isEmpty() ? fieldName : currentPath.get() + "." + fieldName;
        
        // 判断当前字段是否需要保留
        boolean isIncluded = includedFields.contains(fullPath)
                || includedFields.stream().anyMatch(f -> f.startsWith(fullPath + "."));
        return !isIncluded;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }

    // 用于嵌套对象序列化时更新路径
    public void setCurrentPath(String path) {
        currentPath.set(path);
    }
}

步骤2:实现自定义TypeAdapterFactory

处理嵌套对象的路径传递:

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 DynamicTypeAdapterFactory implements TypeAdapterFactory {
    private final DynamicIncludeStrategy strategy;

    public DynamicTypeAdapterFactory(DynamicIncludeStrategy strategy) {
        this.strategy = strategy;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                out.beginObject();
                Field[] fields = value.getClass().getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    if (!strategy.shouldSkipField(new FieldAttributes(field))) {
                        out.name(fieldName);
                        Object fieldValue = field.get(value);
                        if (fieldValue != null && !isBasicType(field.getType())) {
                            // 处理嵌套对象,更新当前路径
                            String prevPath = strategy.currentPath.get();
                            String newPath = prevPath.isEmpty() ? fieldName : prevPath + "." + fieldName;
                            strategy.setCurrentPath(newPath);
                            gson.toJson(fieldValue, field.getType(), out);
                            strategy.setCurrentPath(prevPath);
                        } else {
                            gson.toJson(fieldValue, field.getType(), out);
                        }
                    }
                }
                out.endObject();
            }

            @Override
            public T read(JsonReader in) throws IOException {
                return delegate.read(in);
            }

            // 判断是否是基础类型(不需要嵌套处理)
            private boolean isBasicType(Class<?> clazz) {
                return clazz.isPrimitive() || clazz.equals(String.class) 
                        || Number.class.isAssignableFrom(clazz) 
                        || Boolean.class.equals(clazz);
            }
        };
    }
}

步骤3:序列化工具方法

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class GsonSerializerUtil {
    public static String serializeModelA(ModelA modelA, List<String> includeFields) {
        Set<String> includedSet = new HashSet<>(includeFields);
        DynamicIncludeStrategy strategy = new DynamicIncludeStrategy(includedSet);
        Gson gson = new GsonBuilder()
                .setExclusionStrategies(strategy)
                .registerTypeAdapterFactory(new DynamicTypeAdapterFactory(strategy))
                .create();
        return gson.toJson(modelA);
    }
}

方案优势总结

  1. 完全符合需求:
    • 动态支持任意字段列表,无需创建大量DTO
    • 不修改原模型类的注解或访问修饰符
    • 性能优秀:Jackson方案基于内置机制,开销极小;Gson方案性能略逊但也满足大多数场景
  2. 扩展性强:可以轻松扩展支持更复杂的字段规则(比如通配符、正则匹配)

内容的提问来源于stack exchange,提问作者Parth Prajapati

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 04:47:36