Java两项目ObjectMapper响应不一致问题求助
两个Java项目需根据请求头配置响应JSON键的格式,自定义了JsonUtils类,通过以下方法生成响应JSON:
public String getResponseJson(String casing, Object response) { if (casing.equals(SNAKE_CASE)) { return JsonUtils.toJson(response); } else { return JsonUtils.toCamelCaseJson(response); } }
第一个项目要求请求JSON为蛇形命名,添加了如下WebMvc配置:
@Configuration @EnableWebMvc @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { @Autowired private ObjectMapper objectMapper; @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); WebMvcConfigurer.super.configureMessageConverters(converters); } }
添加配置后,API返回的是字符串化的JSON(带引号和转义符),而非标准JSON格式;第二个项目未添加该配置,可正常返回标准JSON。怀疑是Hibernate引入的ObjectMapper导致问题,且无法移除Hibernate依赖。
你的getResponseJson方法返回String类型,而添加的MappingJackson2HttpMessageConverter会对这个字符串再执行一次JSON序列化——相当于把JSON字符串本身当成一个字符串值来处理,最终就会输出带引号和转义符的结果。第二个项目没有自定义转换器,Spring使用默认的字符串处理逻辑,直接将字符串作为响应体返回,所以格式正常。
方案1:返回对象而非字符串,动态配置命名策略
不再手动转JSON字符串,而是返回原始响应对象,通过MappingJacksonValue包装后动态指定命名策略,让Spring的消息转换器完成序列化:
@Autowired private ObjectMapper objectMapper; public ResponseEntity<?> getApiResponse(@RequestHeader("casing") String casing) { // 构造你的响应数据对象 Object responseData = buildResponseData(); MappingJacksonValue jacksonValue = new MappingJacksonValue(responseData); // 根据请求头切换命名策略 if (SNAKE_CASE.equals(casing)) { jacksonValue.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); } else { jacksonValue.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE); } return ResponseEntity.ok(jacksonValue); }
这种方式无需依赖自定义的JsonUtils,直接利用Jackson原生能力,避免二次序列化问题。
方案2:避免String的二次序列化(兼容现有JsonUtils)
如果要继续使用JsonUtils生成字符串,可将响应直接以字节数组返回,绕过Jackson的字符串序列化逻辑:
import org.springframework.http.MediaType; import java.nio.charset.StandardCharsets; public ResponseEntity<byte[]> getResponseJson(String casing, Object response) { String jsonStr; if (casing.equals(SNAKE_CASE)) { jsonStr = JsonUtils.toJson(response); } else { jsonStr = JsonUtils.toCamelCaseJson(response); } return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(jsonStr.getBytes(StandardCharsets.UTF_8)); }
Spring会直接将字节数组作为响应体输出,不会再做额外序列化,最终得到标准JSON。
方案3:调整WebMvc转换器配置
如果必须保留自定义MappingJackson2HttpMessageConverter,可移除默认的Jackson转换器,避免重复处理:
@Configuration @EnableWebMvc @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final ObjectMapper objectMapper; @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 移除默认的Jackson转换器 converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter); // 添加自定义转换器 converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); WebMvcConfigurer.super.configureMessageConverters(converters); } }
同时,确保JsonUtils使用的ObjectMapper和配置中注入的是同一个实例,避免序列化行为不一致。
内容的提问来源于stack exchange,提问作者Amit Gaurav

