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

Jackson如何为任意对象所有属性名(含嵌套)动态追加指定后缀?

问题背景

现有如下Java类定义:

@Value
public class Example {
    @JsonProperty("StrValue")
    String strValue;
    @JsonProperty("Internal")
    Internal internal;
    
    @Value
    public static class Internal {
        @JsonProperty("InternalValue")
        String internalValue;
    }
}

默认序列化后得到的JSON格式如下:

"Example" : {
  "StrValue": "v1",
  "Internal": {
    "InternalValue": "v2"
  }
}

需要生成两种不同版本的序列化结果:一种为所有属性名(含根节点、嵌套对象属性)追加字符串First后缀,另一种追加Second后缀,预期格式如下:

"ExampleFirst" : {
  "StrValueFirst": "v1",
  "InternalFirst": {
    "InternalValueFirst": "v2"
  }
}

要求该能力支持任意类的任意对象,且对所有嵌套属性生效。


实现方案

你可以通过Jackson自带的PropertyNamingStrategy + 根节点映射配置实现,比自定义BeanSerializerModifier复杂度低很多,完全满足通用、嵌套生效的需求。

步骤1:自定义后缀追加命名策略

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;

public class SuffixAppendNamingStrategy extends PropertyNamingStrategy {
    private final String suffix;

    public SuffixAppendNamingStrategy(String suffix) {
        this.suffix = suffix;
    }

    @Override
    public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
        return defaultName + suffix;
    }

    @Override
    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
        return defaultName + suffix;
    }

    @Override
    public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
        return defaultName + suffix;
    }

    @Override
    public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) {
        return defaultName + suffix;
    }
}

步骤2:自定义根节点后缀映射

import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.RootNameMapper;

public class SuffixAppendRootNameMapper implements RootNameMapper {
    private final String suffix;

    public SuffixAppendRootNameMapper(String suffix) {
        this.suffix = suffix;
    }

    @Override
    public String getRootName(MapperConfig<?> config, AnnotatedClass annotatedClass) {
        String defaultRootName = config.getAnnotationIntrospector().findRootName(annotatedClass).getSimpleName();
        return defaultRootName == null ? annotatedClass.getRawType().getSimpleName() + suffix : defaultRootName + suffix;
    }
}

步骤3:封装Mapper生成工具

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class SuffixSerializerFactory {
    private static final ObjectMapper BASE_MAPPER = new ObjectMapper();

    public static ObjectMapper getMapperWithSuffix(String suffix) {
        ObjectMapper mapper = BASE_MAPPER.copy();
        // 开启根节点包裹
        mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
        // 配置属性命名策略
        mapper.setPropertyNamingStrategy(new SuffixAppendNamingStrategy(suffix));
        // 配置根节点名称映射
        mapper.setRootNameMapper(new SuffixAppendRootNameMapper(suffix));
        return mapper;
    }
}

测试使用

// 构造测试对象
Example.Internal internal = new Example.Internal("v2");
Example example = new Example("v1", internal);

// 生成带First后缀的JSON
ObjectMapper firstMapper = SuffixSerializerFactory.getMapperWithSuffix("First");
String firstResult = firstMapper.writerWithDefaultPrettyPrinter().writeValueAsString(example);

// 生成带Second后缀的JSON
ObjectMapper secondMapper = SuffixSerializerFactory.getMapperWithSuffix("Second");
String secondResult = secondMapper.writerWithDefaultPrettyPrinter().writeValueAsString(example);

注意事项

  • ObjectMapper是线程安全的,你可以将不同后缀的Mapper实例做单例缓存,避免重复创建开销
  • 该实现会优先保留@JsonProperty指定的属性名再追加后缀,和原有序列化逻辑完全兼容
  • 如果不需要根节点包裹,仅需属性加后缀,移除enable(SerializationFeature.WRAP_ROOT_VALUE)和RootNameMapper的配置即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 09:36:00