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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 13:50:24