如何用Jackson同时反序列化驼峰式与下划线式命名的JSON?
解决Jackson兼容驼峰/下划线JSON反序列化+序列化仅输出下划线的问题
要实现一个ObjectMapper同时支持两种命名格式的反序列化,且序列化固定输出下划线格式,有两种可行方案,以下是具体实现:
方案一:全局自定义命名策略(适合多字段场景)
通过自定义PropertyNamingStrategy,让序列化时使用下划线规则,反序列化时自动兼容驼峰和下划线格式:
1. 自定义命名策略类
import com.fasterxml.jackson.databind.MapperConfig; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.introspect.AnnotatedField; import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.PropertyNamingStrategies; public class MixedNamingStrategy extends PropertyNamingStrategy { // 驼峰命名策略(用于转换下划线到驼峰) private final PropertyNamingStrategy camelCase = PropertyNamingStrategies.LOWER_CAMEL_CASE; // 下划线命名策略(用于序列化输出) private final PropertyNamingStrategy snakeCase = PropertyNamingStrategies.SNAKE_CASE; // 序列化字段时使用下划线策略 @Override public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) { return snakeCase.nameForField(config, field, defaultName); } // 序列化getter方法对应字段时使用下划线策略 @Override public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) { return snakeCase.nameForGetterMethod(config, method, defaultName); } // 序列化setter方法对应字段时使用下划线策略 @Override public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) { return snakeCase.nameForSetterMethod(config, method, defaultName); } // 反序列化时统一转换字段名为驼峰格式,兼容两种输入 @Override public String translate(String input) { // 先把输入转为下划线格式,再转回驼峰——无论输入是驼峰还是下划线,最终都得到实体类的驼峰字段名 String snakeForm = snakeCase.translate(input); return camelCase.translate(snakeForm); } }
2. 配置ObjectMapper并测试
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonProcessingException; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; public class Test { public static void main(final String[] args) throws JsonProcessingException { final ObjectMapper objectMapper = new ObjectMapper(); // 应用自定义命名策略 objectMapper.setPropertyNamingStrategy(new MixedNamingStrategy()); // 忽略未知字段,避免非目标字段导致报错 objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 测试驼峰JSON反序列化 Parent camelCaseResult = objectMapper.readValue("{\"testField\":\"test\"}", Parent.class); System.out.println("驼峰JSON反序列化结果:" + camelCaseResult); // 测试下划线JSON反序列化 Parent snakeCaseResult = objectMapper.readValue("{\"test_field\":\"test\"}", Parent.class); System.out.println("下划线JSON反序列化结果:" + snakeCaseResult); // 测试序列化(输出下划线格式) String serializedJson = objectMapper.writeValueAsString(camelCaseResult); System.out.println("序列化输出(下划线格式):" + serializedJson); } @Data @AllArgsConstructor @NoArgsConstructor static class Parent { String testField; } }
方案二:字段级别名配置(适合少字段场景)
如果实体类字段不多,直接给字段添加@JsonAlias注解指定别名,同时设置序列化策略为下划线格式:
1. 修改实体类
@Data @AllArgsConstructor @NoArgsConstructor static class Parent { // 允许反序列化时识别test_field别名 @JsonAlias("test_field") String testField; }
2. 配置ObjectMapper并测试
public static void main(final String[] args) throws JsonProcessingException { final ObjectMapper objectMapper = new ObjectMapper(); // 序列化时使用下划线命名策略 objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 测试两种格式反序列化 System.out.println(objectMapper.readValue("{\"testField\":\"test\"}", Parent.class)); System.out.println(objectMapper.readValue("{\"test_field\":\"test\"}", Parent.class)); // 测试序列化输出下划线格式 System.out.println(objectMapper.writeValueAsString(new Parent("test"))); }
两种方案对比
- 自定义命名策略:全局生效,无需逐个字段配置,适合字段数量多的场景,维护成本低。
- @JsonAlias注解:简单直观,适合字段少的场景,可针对特定字段灵活配置别名。
内容的提问来源于stack exchange,提问作者Suzuki Yuu
相关产品推荐
相关产品推荐

