ObjectMapper
是 Jackson 库的核心类,专门用于处理 JSON 数据的序列化(Java 对象 → JSON)和反序列化(JSON → Java 对象)。在你提供的代码中,它解决了字符串响应特殊处理的关键问题。
一、为什么需要 ObjectMapper?
问题背景
java
if (body instanceof String) {
return mapper.writeValueAsString(Result.success(body));
}
这段代码处理的是当控制器返回纯字符串时的特殊情况。在 Spring MVC 中,不同类型的返回值有不同的处理方式:
返回值类型 | 处理方式 | 问题 |
---|---|---|
对象/集合 | 自动使用 JSON 转换器 | 无 |
字符串 | 使用字符串转换器 | 无法自动包装为 JSON |
具体问题演示
假设有一个控制器:
java
@RestController
public class ExampleController {
@GetMapping("/string")
public String getString() {
return "hello"; // 返回纯字符串
}
}
没有 ObjectMapper 的情况:
beforeBodyWrite
返回Result.success("hello")
- Spring 尝试使用
StringHttpMessageConverter
处理 - 因为
Result
不是字符串 → 类型转换错误!
二、ObjectMapper 如何解决这个问题
解决方案
java
mapper.writeValueAsString(Result.success(body));
分步解析
java
创建包装对象:
Result result = Result.success("hello");
// 得到:{code:200, msg:"success", data:"hello"}
手动序列化为 JSON 字符串:
String json = mapper.writeValueAsString(result);
// 得到:'{"code":200,"msg":"success","data":"hello"}'
-
最终返回字符串结果:
- 符合
StringHttpMessageConverter
的预期 - 客户端收到标准 JSON 格式
- 符合
序列化过程图解
java
Java对象: Result
├─ code: 200
├─ msg: "success"
└─ data: "hello"
↓ ObjectMapper 序列化
JSON字符串: '{"code":200,"msg":"success","data":"hello"}'
三、ObjectMapper 的核心能力
1. 序列化配置
java
// 创建时可配置(代码中通常静态初始化)
private static ObjectMapper mapper = new ObjectMapper();
// 常用配置(可添加到静态初始化块)
static {
// 美化输出(开发环境)
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// 空值不参与序列化
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 日期格式标准化
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
2.支持复杂类型
java
// 嵌套对象
Result result = Result.success(new User("Alice", 25));
String json = mapper.writeValueAsString(result);
// 输出:{"code":200,"msg":"success","data":{"name":"Alice","age":25}}
// 集合类型
List<String> list = Arrays.asList("A", "B", "C");
String json = mapper.writeValueAsString(Result.success(list));
// 输出:{"code":200,"msg":"success","data":["A","B","C"]}
3. 自定义序列化(高级)
java
// 自定义序列化器
public class MoneySerializer extends StdSerializer<BigDecimal> {
public MoneySerializer() {
super(BigDecimal.class);
}
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider) {
gen.writeString(value.setScale(2) + "元");
}
}
// 注册自定义序列化器
mapper.registerModule(new SimpleModule()
.addSerializer(BigDecimal.class, new MoneySerializer()));
// 使用效果
Result result = Result.success(new BigDecimal("123.456"));
String json = mapper.writeValueAsString(result);
// 输出:{"code":200,"msg":"success","data":"123.46元"}
四、实际应用场景
场景 1:统一处理日期格式
java
static {
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
}
// 控制器返回
@GetMapping("/date")
public Date getDate() {
return new Date(); // 返回日期对象
}
// 处理结果
// 原始输出:1689987600000(时间戳)
// 处理后输出:"2023-07-22"
场景 2:处理枚举类型
java
public enum Status {
ACTIVE, INACTIVE
}
// 默认序列化
Status.ACTIVE → "ACTIVE"(枚举名)
// 自定义序列化
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
// 在枚举中添加toString
public enum Status {
ACTIVE("激活"), INACTIVE("禁用");
private String desc;
@Override
public String toString() {
return desc;
}
}
// 输出结果:"激活"
场景 3:处理特殊字符
java
String text = "包含<特殊>字符&符号";
Result result = Result.success(text);
// 未处理时:可能破坏JSON结构
// 处理后:自动转义为"包含\u003C特殊\u003E字符\u0026符号"
完整的最佳实践
java
@Slf4j
@ControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {
// 静态初始化(线程安全)
private static final ObjectMapper mapper = new ObjectMapper();
static {
// 基础配置
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 注册Java 8时间模块
mapper.registerModule(new JavaTimeModule());
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// 排除特定注解或类型
return !returnType.hasMethodAnnotation(IgnoreWrap.class);
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 1. 已经是包装类型则直接返回
if (body instanceof Result) {
return body;
}
// 2. 处理空响应
if (body == null) {
return Result.success();
}
// 3. 特殊处理String类型
if (body instanceof String) {
// 设置正确的Content-Type
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return mapper.writeValueAsString(Result.success(body));
}
// 4. 添加请求ID到响应头
String requestId = request.getHeaders().getFirst("X-Request-ID");
if (requestId != null) {
response.getHeaders().add("X-Request-ID", requestId);
}
// 5. 默认包装
return Result.success(body);
}
}
总结
ObjectMapper 在统一响应处理中扮演着JSON 序列化引擎的角色,核心解决了两个关键问题:
- 统一响应格式:将各种类型的数据包装为标准结构
- 特殊类型处理:解决字符串返回值无法自动包装的问题
通过合理配置 ObjectMapper,可以实现:
- 日期、枚举等特殊类型的格式化
- 空值过滤、缩进美化等输出控制
- 复杂对象和集合的序列化
- 自定义序列化逻辑