使用Builder模式时如何实现Jackson驼峰键自动转蛇形键
问题根因
@JsonProperty注解会同时控制序列化、反序列化的字段名,优先级高于全局配置的PropertyNamingStrategy,你为了匹配上游返回的驼峰键加了@JsonProperty("myBanner"),导致序列化输出时也强制使用了驼峰命名,覆盖了全局蛇形配置。
以下三种方案均支持保留Builder用法,可按需选择:
方案1:最小改动,用@JsonAlias替代@JsonProperty
@JsonAlias仅在反序列化阶段作为备用键名匹配,不会影响序列化规则,序列化仍会遵循全局蛇形命名策略输出下划线格式的键。
修改后的实体类示例:
@Getter @Builder @JsonInclude(JsonInclude.Include.NON_NULL) @JsonDeserialize(builder = Layout.LayoutBuilder.class) public class Layout { // 仅反序列化时识别myBanner,序列化自动输出my_banner @JsonAlias("myBanner") private final String myBanner; }
方案2:架构优化,拆分序列化/反序列化ObjectMapper
你当前存在两个互斥的JSON格式诉求:反序列化上游接口的驼峰JSON、序列化返回给客户端的蛇形JSON,共用同一个ObjectMapper会导致规则冲突。
可以给调用上游接口的RestTemplate/WebClient单独配置专用的ObjectMapper,全局MVC的ObjectMapper保留原有蛇形配置即可,实体类无需加任何字段级注解:
// 专门用来调用上游接口的RestTemplate配置 @Bean public RestTemplate upstreamRestTemplate() { RestTemplate restTemplate = new RestTemplate(); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); // 该ObjectMapper仅做上游驼峰JSON的反序列化,不需要配置蛇形命名 converter.setObjectMapper(new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)); // 提高该转换器的优先级 restTemplate.getMessageConverters().add(0, converter); return restTemplate; }
调用上游接口时使用这个自定义的upstreamRestTemplate即可。
方案3:兜底方案,手动递归转换JSON键
如果不想调整现有注解和配置,可在返回响应前将对象转为JSON节点,递归替换所有驼峰键为蛇形键再输出,工具类示例:
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.CaseFormat; public class CamelToSnakeConverter { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static JsonNode convert(JsonNode node) { if (node.isObject()) { ObjectNode resNode = OBJECT_MAPPER.createObjectNode(); node.fields().forEachRemaining(entry -> { String snakeKey = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, entry.getKey()); resNode.set(snakeKey, convert(entry.getValue())); }); return resNode; } else if (node.isArray()) { var arrayNode = OBJECT_MAPPER.createArrayNode(); node.elements().forEachRemaining(element -> arrayNode.add(convert(element))); return arrayNode; } return node; } }
内容的提问来源于stack exchange,提问作者Fllappy
相关产品推荐
相关产品推荐

