如何使用Jackson将空JSON数组反序列化为空TreeMap<String, MyOtherClass>对象
解决方案
问题根源
Jackson默认不支持将JSON数组类型直接反序列化为Java Map类型,哪怕是空数组也会触发类型不匹配异常,这就是你看到MismatchedInputException的核心原因。你之前的尝试存在两处问题:开启ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT只会将空数组转为null,无法得到你需要的空TreeMap;你写的setOtherThings是无参方法,Jackson无法识别为合法的Setter,自然不会生效。
推荐方案1:字段级自定义反序列化器(无全局影响)
这个方案只针对otherThings字段生效,不会改动其他业务逻辑的反序列化规则:
- 首先实现自定义反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import java.io.IOException; import java.util.TreeMap; public class EmptyArrayAsEmptyTreeMapDeserializer extends StdDeserializer<TreeMap<String, MyOtherClass>> { public EmptyArrayAsEmptyTreeMapDeserializer() { super(TreeMap.class); } @Override public TreeMap<String, MyOtherClass> deserialize(JsonParser parser, DeserializationContext context) throws IOException { // 处理空数组场景 if (parser.isExpectedStartArrayToken()) { // 跳过数组结束标记 parser.nextToken(); // 直接返回空TreeMap return new TreeMap<>(); } // 正常对象场景走默认Map反序列化逻辑 if (parser.isExpectedStartObjectToken()) { return context.readValue(parser, context.getTypeFactory().constructMapType(TreeMap.class, String.class, MyOtherClass.class)); } // 其他非法类型抛出异常 throw MismatchedInputException.from(parser, TreeMap.class, "只能从JSON对象或空数组反序列化TreeMap"); } }
- 调整
MyClass中otherThings字段的注解,同时删除你原来写的无参setOtherThings方法:
@JsonSerialize(contentAs = MyOtherClass.class) // 替换原来的@JsonDeserialize注解,绑定自定义反序列化器 @JsonDeserialize(using = EmptyArrayAsEmptyTreeMapDeserializer.class) TreeMap<String, MyOtherClass> otherThings = new TreeMap<>();
方案2:Setter兼容处理(不需要新增类)
如果你不想额外新增反序列化器类,可以修改Setter方法自行处理类型兼容,还能避免覆盖已有数据:
// 移除字段上的@JsonDeserialize注解 TreeMap<String, MyOtherClass> otherThings = new TreeMap<>(); @JsonSetter("otherThings") public void setOtherThings(JsonNode node) throws IOException { // 空数组场景直接保留已初始化的空TreeMap if (node.isArray() && node.isEmpty()) { return; } // 正常对象场景才做转换 if (node.isObject()) { ObjectMapper mapper = new ObjectMapper(); TreeMap<String, MyOtherClass> parsedValue = mapper.convertValue(node, new TypeReference<TreeMap<String, MyOtherClass>>() {}); // 仅当解析结果非空时覆盖,避免清空已有数据 if (parsedValue != null && !parsedValue.isEmpty()) { this.otherThings = parsedValue; } } }
方案3:全局配置(适合多字段需要相同规则的场景)
如果你项目中多处存在需要将空数组转为空Map的场景,可以统一配置全局ObjectMapper:
ObjectMapper mapper = new ObjectMapper(); // 先开启空数组转null配置 mapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT); // 注册反序列化修改器,所有Map类型字段为null时自动初始化为空TreeMap mapper.registerModule(new SimpleModule() .setDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { if (TreeMap.class.isAssignableFrom(beanDesc.getType().getRawClass())) { return new JsonDeserializer<>() { @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { Object result = deserializer.deserialize(p, ctxt); return result == null ? new TreeMap<>() : result; } }; } return deserializer; } }));
注意:该方案会影响所有TreeMap类型字段的反序列化规则,如果你有其他TreeMap字段需要不同的初始化逻辑不要使用该方案。
内容的提问来源于stack exchange,提问作者Scott Crooks
相关产品推荐
相关产品推荐

