Spring Boot如何为不同的控制器使用不同的ObjectMapper进行序列化?
背景:这是一个较旧的Spring Boot 2.4.5项目。新增了一个与智能代理API相关的模块。
我们希望统一 com.xxx.controller.agent 目录中所有控制器的API返回的JSON格式,但不影响旧控制器的JSON返回值。
class Data{
private BigDecimal d = new BigDecimal("1.23000")
}
// agent controller return json
=> {d: '1.23'}
// old controller return json
=> {d: 1.23}
我该如何实现?
解决方案
构建一个序列化器,去除尾随的零,并将 BigDecimal 输出为普通字符串:
package com.xxx.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;
public class AgentBigDecimalSerializer extends JsonSerializer<BigDecimal>
{
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (value != null)
gen.writeString(value.stripTrailingZeros().toPlainString());
else gen.writeNull();
}
}
使用 ResponseBodyAdvice 仅拦截来自 com.xxx.controller.agent 包的响应,这样就能在JSON被写入之前动态注入一个专门的 ObjectMaper,并用你的序列化器对其进行配置:
package com.xxx.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.math.BigDecimal;
@RestControllerAdvice(basePackages = "com.xxx.controller.agent")
public class AgentResponseAdvice implements ResponseBodyAdvice<Object>
{
private final MappingJackson2HttpMessageConverter agentConverter;
public AgentResponseAdvice()
{
// Create an isolated ObjectMapper instance for agent controllers
ObjectMapper agentMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(BigDecimal.class, new AgentBigDecimalSerializer());
agentMapper.registerModule(module);
this.agentConverter = new MappingJackson2HttpMessageConverter(agentMapper);
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType)
{
// Only intercept if Spring is planning to use Jackson for the response
return MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response)
{
if (body == null)
return null;
// Force Spring to use your converter instead of the global one
try {
agentConverter.write(body, selectedContentType, response);
} catch (Exception e) {
throw new RuntimeException("Failed to serialize agent API response", e);
}
// Return null to tell Spring that response has already been handled
return null;
}
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。