如何通过Jackson ObjectMapper将JSON直接反序列化为Map<String, Set<Key>>
问题描述
现有如下结构的JSON对象:
{ "key1": { "code1": "name1", "code2": "name2", ... }, "key2": { "code1": "name1", "code2": "name2", ... }, ... }
需要将其直接反序列化为Map<String, Set<Key>>类型,而非Jackson默认支持的Map<String, Map<String, String>>。Key类定义如下:
public class Key{ private String code; private String Name; }
自定义了反序列化器但运行时报错:
public static class KeyListDeserializer extends JsonDeserializer<Set<Key>> { @Override public Set<Key> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { Set<Key> keyList = new HashSet<>(); JsonNode rootNode = jp.getCodec().readTree(jp); Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> field = fields.next(); String code = field.getKey(); String name = field.getValue().asText(); Key key = new Key(); key.setCode(code); key.setName(name); keyList.add(key); } return keyList; } }
报错信息:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.HashSet<um.Test1$Key>` from Object value (token `JsonToken.START_OBJECT`)
解决方案
报错核心原因:Jackson默认认为Set类型对应JSON数组,但你的JSON中每个分组(如key1)的值是JSON对象而非数组,因此需要明确告知Jackson为Set<Key>类型绑定自定义反序列化器,并正确配置ObjectMapper。
步骤1:修正Key类(必要)
原Key类中Name首字母大写,不符合Java驼峰命名规范,Jackson默认会按小写首字母匹配属性,同时必须添加getter/setter方法,否则Jackson无法完成属性赋值:
public class Key { private String code; private String name; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
步骤2:注册自定义反序列化器并完成反序列化
通过SimpleModule将自定义反序列化器绑定到Set<Key>类型,借助TypeReference解决Java泛型擦除问题,直接反序列化为目标类型:
public class Main { public static void main(String[] args) throws IOException { String json = "{\"key1\":{\"code1\":\"name1\",\"code2\":\"name2\"},\"key2\":{\"code3\":\"name3\"}}"; // 创建模块并注册反序列化器 SimpleModule module = new SimpleModule(); module.addDeserializer(new TypeReference<Set<Key>>() {}, new KeyListDeserializer()); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(module); // 直接反序列化为Map<String, Set<Key>> Map<String, Set<Key>> result = objectMapper.readValue( json, new TypeReference<Map<String, Set<Key>>>() {} ); // 验证结果 for (Map.Entry<String, Set<Key>> entry : result.entrySet()) { System.out.println("分组:" + entry.getKey()); for (Key key : entry.getValue()) { System.out.printf(" Code: %s, Name: %s%n", key.getCode(), key.getName()); } } } // 自定义反序列化器(保留原有逻辑即可) public static class KeyListDeserializer extends JsonDeserializer<Set<Key>> { @Override public Set<Key> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { Set<Key> keySet = new HashSet<>(); JsonNode rootNode = jp.getCodec().readTree(jp); Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> field = fields.next(); Key key = new Key(); key.setCode(field.getKey()); key.setName(field.getValue().asText()); keySet.add(key); } return keySet; } } }
关键说明
- 使用
TypeReference指定泛型类型:由于Java泛型擦除,直接传入Map.class无法让Jackson识别内层的Set<Key>泛型参数,必须用TypeReference包装。 - 模块注册的优势:这种方式属于全局配置,后续所有需要反序列化为
Set<Key>的场景都会自动使用该反序列化器,无需重复注解。
内容的提问来源于stack exchange,提问作者Surya Kumar
相关产品推荐
相关产品推荐

