如何在Jackson反序列化时将数组自动转为以id为键的HashMap?
Jackson反序列化时自动将List转为HashMap的实现方法
问题场景
现有如下JSON数据:
{ "default": "ignore-me", "catalog": [ { "id": "object-1", "name": "Object 1" }, { "id": "object-3", "name": "Object 3" }, { "id": "object-2", "name": "Object 2" } ] }
目前使用Jackson通过以下代码解析:
ObjectMapper mapper = new ObjectMapper(); Catalogs catalogs = mapper.readValue(new File("/catalogs.json"), Catalogs.class);
对应的Java模型类如下:
Catalogs.java
import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public class Catalogs { @JsonProperty("default") private String defaultField; private List<Catalog> catalogList; // getters / setters }
Catalog.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Catalog { private String id; private String name; // getters / setters }
需求是将List<Catalog> catalogList转换为HashMap<String, Catalog>,这样就能通过已知的id快速获取对应条目,实现类似Catalog catalog = catalogs.getCatalogMap().get("object-1");的调用方式,问Jackson能否在反序列化过程中自动完成该类型转换?
实现方案
Jackson完全支持在反序列化过程中自动完成这个转换,有两种简洁的实现方式:
方式一:利用@JsonCreator构造函数转换
直接修改Catalogs类,通过带@JsonCreator注解的构造函数接收List参数,内部直接转为HashMap:
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; import java.util.List; import java.util.Map; public class Catalogs { @JsonProperty("default") private String defaultField; private Map<String, Catalog> catalogMap; // 用于Jackson反序列化的构造函数 @JsonCreator public Catalogs( @JsonProperty("default") String defaultField, @JsonProperty("catalog") List<Catalog> catalogList) { this.defaultField = defaultField; this.catalogMap = new HashMap<>(); // 遍历List,将id作为key存入Map for (Catalog catalog : catalogList) { this.catalogMap.put(catalog.getId(), catalog); } } // 保留无参构造函数,兼容Jackson的默认序列化/其他场景 public Catalogs() {} // getters / setters public Map<String, Catalog> getCatalogMap() { return catalogMap; } public void setCatalogMap(Map<String, Catalog> catalogMap) { this.catalogMap = catalogMap; } // 其他getter/setter... }
这种方式无需额外编写反序列化器,代码简洁直观,Jackson会自动把JSON中的catalog数组注入到构造函数的List参数中,完成转换。
方式二:自定义反序列化器
如果需要更灵活的控制(比如处理空值、自定义key生成规则等),可以编写自定义反序列化器:
- 首先修改
Catalogs类的字段类型,并添加注解指定使用自定义反序列化器:
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.Map; public class Catalogs { @JsonProperty("default") private String defaultField; @JsonProperty("catalog") @JsonDeserialize(using = CatalogListToMapDeserializer.class) private Map<String, Catalog> catalogMap; // getters / setters }
- 编写自定义反序列化器
CatalogListToMapDeserializer.java:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class CatalogListToMapDeserializer extends JsonDeserializer<Map<String, Catalog>> { @Override public Map<String, Catalog> deserialize(JsonParser parser, DeserializationContext context) throws IOException { ObjectMapper mapper = (ObjectMapper) parser.getCodec(); JsonNode node = mapper.readTree(parser); Map<String, Catalog> catalogMap = new HashMap<>(); Iterator<JsonNode> elements = node.elements(); while (elements.hasNext()) { Catalog catalog = mapper.treeToValue(elements.next(), Catalog.class); if (catalog != null && catalog.getId() != null) { catalogMap.put(catalog.getId(), catalog); } } return catalogMap; } }
两种方式都能实现需求,之后就可以通过catalogs.getCatalogMap().get("object-1")直接获取对应Catalog对象了。
内容的提问来源于stack exchange,提问作者bobbyrne01
相关产品推荐
相关产品推荐

