使用Jackson自定义反序列化器处理美团API中不规范JSON字段的兼容方案
在对接美团开放平台API时,开发者常遇到返回JSON结构不规范的问题:例如字段类型不一致(字符串与数字混用)、布尔值以"1"/"0"表示、时间格式非ISO8601、甚至同一字段在不同接口下含义不同。若直接使用标准Jackson反序列化,极易抛出JsonMappingException。本文将基于baodanbao.com.cn.*包结构,展示如何通过自定义JsonDeserializer实现健壮的兼容解析。
1. 问题场景示例
美团某订单接口返回如下片段:
{
"order_id": "123456",
"is_valid": "1",
"amount": "89.50",
"create_time": "2024-03-05 14:30:00"
}
理想Java类应为:
public class MeituanOrder {
private Long orderId;
private Boolean isValid;
private BigDecimal amount;
private LocalDateTime createTime;
}
但默认反序列化会因类型不匹配失败。需定制逻辑。

2. 自定义Boolean反序列化器
处理 "1"/"0" 或 "true"/"false" 混合情况:
package baodanbao.com.cn.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class FlexibleBooleanDeserializer extends JsonDeserializer<Boolean> {
@Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getText().trim();
if (value.isEmpty()) return null;
if ("1".equals(value) || "true".equalsIgnoreCase(value)) {
return true;
}
if ("0".equals(value) || "false".equalsIgnoreCase(value)) {
return false;
}
throw new IllegalArgumentException("无法解析布尔值: " + value);
}
}
3. 自定义数值反序列化器(支持字符串转BigDecimal)
package baodanbao.com.cn.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.math.BigDecimal;
public class StringToBigDecimalDeserializer extends JsonDeserializer<BigDecimal> {
@Override
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getText().trim();
if (value.isEmpty()) return null;
try {
return new BigDecimal(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("无法解析金额: " + value, e);
}
}
}
4. 自定义LocalDateTime反序列化器(兼容空格分隔时间)
package baodanbao.com.cn.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class FlexibleLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getText().trim();
if (value.isEmpty()) return null;
try {
return LocalDateTime.parse(value, FORMATTER);
} catch (DateTimeParseException e) {
throw new IllegalArgumentException("无法解析时间: " + value, e);
}
}
}
5. 在实体类中应用自定义反序列化器
package baodanbao.com.cn.model.meituan;
import baodanbao.com.cn.deserializer.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class MeituanOrder {
@JsonDeserialize(using = StringToLongDeserializer.class)
private Long orderId;
@JsonDeserialize(using = FlexibleBooleanDeserializer.class)
private Boolean isValid;
@JsonDeserialize(using = StringToBigDecimalDeserializer.class)
private BigDecimal amount;
@JsonDeserialize(using = FlexibleLocalDateTimeDeserializer.class)
private LocalDateTime createTime;
// getters and setters
}
其中StringToLongDeserializer实现类似:
package baodanbao.com.cn.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class StringToLongDeserializer extends JsonDeserializer<Long> {
@Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText().trim();
return text.isEmpty() ? null : Long.parseLong(text);
}
}
6. 全局注册(可选)
若多个类需复用,可在ObjectMapper中全局注册:
package baodanbao.com.cn.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Boolean.class, new FlexibleBooleanDeserializer());
module.addDeserializer(BigDecimal.class, new StringToBigDecimalDeserializer());
mapper.registerModule(module);
return mapper;
}
}
但注意:全局注册可能影响其他接口,建议优先使用字段级@JsonDeserialize。
7. 异常容错增强:忽略非法字段
在配置中开启宽容模式,避免因个别字段异常导致整个响应解析失败:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
// 注册模块...
return mapper;
}
8. 单元测试验证
package baodanbao.com.cn.test;
import baodanbao.com.cn.model.meituan.MeituanOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MeituanOrderDeserializationTest {
@Test
void testDeserializeFromMeituanJson() throws Exception {
String json = """
{
"order_id": "789012",
"is_valid": "1",
"amount": "123.45",
"create_time": "2024-03-05 14:30:00"
}
""";
ObjectMapper mapper = new ObjectMapper();
MeituanOrder order = mapper.readValue(json, MeituanOrder.class);
assertEquals(789012L, order.getOrderId());
assertTrue(order.getIsValid());
assertEquals(new BigDecimal("123.45"), order.getAmount());
assertEquals(LocalDateTime.of(2024, 3, 5, 14, 30, 0), order.getCreateTime());
}
}
本文著作权归吃喝不愁app开发者团队,转载请注明出处!
900

被折叠的 条评论
为什么被折叠?



