Kotlin Moshi适配器如何处理JsonDataException,类型不匹配时设为null
解决JSON类型不匹配解析异常的方案
针对老旧后端返回JSON类型不符(比如契约规定返回字符串却返回对象/空字符串)导致的JsonDataException Expected a string but was BEGIN_OBJECT异常,可通过以下方式将解析错误的字段设为null并忽略错误:
1. 全局配置(一次性解决所有字段)
如果用Jackson作为JSON解析框架,可通过全局配置关闭类型不匹配时的异常抛出,让框架自动将错误字段设为null:
ObjectMapper objectMapper = new ObjectMapper(); // 核心配置:遇到类型不匹配时不抛异常,直接设为null objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_TYPE, false); // 同时忽略未知字段(可选,避免后端新增字段导致解析失败) objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
如果是Spring Boot项目,也可以在配置文件中设置:
spring: jackson: deserialization: fail-on-invalid-type: false fail-on-unknown-properties: false accept-empty-string-as-null-object: true # 把空字符串转为null对象
这种方式最简单,所有类型不匹配的字段都会被自动设为null,无需修改实体类。
2. 单个字段精准处理(仅针对有问题的字段)
如果不想全局生效,只需要处理特定字段,可以给字段添加自定义反序列化器:
比如你的data字段本该是对象但可能返回空字符串,data2本该是字符串但可能返回对象,可这样写实体类和反序列化器:
// 响应实体类 public class ApiResponse { // 处理data字段:空字符串转null,类型不匹配也转null @JsonDeserialize(using = SafeObjectDeserializer.class) private Data data; private String other_data; // 处理data2字段:非字符串类型转null @JsonDeserialize(using = SafeStringDeserializer.class) private String data2; // getter、setter省略 } // 安全对象反序列化器:处理空字符串/类型不匹配 class SafeObjectDeserializer extends StdDeserializer<Data> { public SafeObjectDeserializer() { super(Data.class); } @Override public Data deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { try { JsonToken token = p.currentToken(); if (token == JsonToken.VALUE_STRING && p.getText().isEmpty()) { // 空字符串返回null return null; } else if (token == JsonToken.START_OBJECT) { // 正常解析对象 return p.readValueAs(Data.class); } else { // 其他类型(比如数组、数字)跳过,返回null p.skipChildren(); return null; } } catch (JsonDataException e) { // 捕获解析异常,跳过当前字段返回null p.skipChildren(); return null; } } } // 安全字符串反序列化器:非字符串类型转null class SafeStringDeserializer extends StdDeserializer<String> { public SafeStringDeserializer() { super(String.class); } @Override public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { try { if (p.currentToken() == JsonToken.VALUE_STRING) { return p.getText(); } else { // 遇到非字符串类型(比如对象),跳过并返回null p.skipChildren(); return null; } } catch (JsonDataException e) { p.skipChildren(); return null; } } }
这种方式更灵活,只对标记的字段生效,不影响其他正常字段。
疑问解答
是否需要在适配器中处理?
不是必须的。Jackson的全局配置或字段注解已经能在DTO解析阶段自动处理异常,不需要额外在适配器层写逻辑。如果你的架构中适配器负责数据转换兜底,也可以在这里捕获解析异常后手动设null,但这种方式不如框架自带的配置优雅高效。
是否必须手动解析全部JSON?
完全不需要。上面的方案都是基于Jackson的自动解析能力,只需要针对有问题的字段做特殊配置,其他字段正常自动解析即可。手动全解析不仅繁琐,还容易引入新的错误,优先用框架原生功能解决。
内容的提问来源于stack exchange,提问作者Unii
相关产品推荐
相关产品推荐

