如何用Spring Data MongoDB映射JSON中的多层嵌套Map结构
Spring Data MongoDB 动态嵌套结构映射问题
问题分析
你当前的实体类无法正确映射目标JSON,核心原因是JSON中attributes是数组类型,但你定义的attributes字段是Map<String, Map<String, List<String>>>,两者类型不匹配,直接使用会导致映射失败。
解决方案
根据你是否允许调整JSON结构,分两种方案:
方案1:调整JSON结构(推荐,更简洁)
原JSON的attributes是仅含一个元素的数组,完全可以改成直接的对象结构,适配你期望的Map类型:
{ "key": "key", "attributes": { "type1": { "class1": ["genre1", "genre2"], "class2": ["genre2", "genre3"] } } }
调整后,实体类只需补充必要的无参构造、getter/setter(Spring Data MongoDB需要实例化和赋值)即可正确映射:
@Document("Test") public class Test { @Field("key") private String key; @Field("attributes") private Map<String, Map<String, List<String>>> attributes; // 必须提供无参构造,供MongoDB实例化对象 public Test() {} // Getter & Setter public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Map<String, Map<String, List<String>>> getAttributes() { return attributes; } public void setAttributes(Map<String, Map<String, List<String>>> attributes) { this.attributes = attributes; } }
方案2:不调整JSON结构,自定义转换器
如果必须保留原JSON的数组结构,需要自定义类型转换器,将MongoDB中的List<Document>转换为你期望的Map结构:
- 实现读转换器(将MongoDB文档转换为Java对象):
import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; import org.bson.Document; import java.util.*; import java.util.stream.Collectors; @ReadingConverter public class AttributesReadConverter implements Converter<List<Document>, Map<String, Map<String, List<String>>>> { @Override public Map<String, Map<String, List<String>>> convert(List<Document> source) { Map<String, Map<String, List<String>>> resultMap = new HashMap<>(); for (Document attrDoc : source) { // 遍历数组中的每个类型对象 for (String typeKey : attrDoc.keySet()) { List<Document> classDocs = attrDoc.getList(typeKey, Document.class); Map<String, List<String>> classMap = classDocs.stream() .collect(Collectors.toMap( // 提取classX作为key(每个对象中排除genres的唯一键) doc -> doc.keySet().stream() .filter(k -> !"genres".equals(k)) .findFirst() .orElse(null), // 提取genres列表作为value doc -> doc.getList("genres", String.class) )); resultMap.put(typeKey, classMap); } } return resultMap; } }
- 在实体类中指定使用该转换器:
@Document("Test") public class Test { @Field("key") private String key; @Field("attributes") @Convert(converter = AttributesReadConverter.class) private Map<String, Map<String, List<String>>> attributes; public Test() {} // Getter & Setter public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Map<String, Map<String, List<String>>> getAttributes() { return attributes; } public void setAttributes(Map<String, Map<String, List<String>>> attributes) { this.attributes = attributes; } }
- 注册转换器(Spring Boot 2.x+通常会自动扫描
@ReadingConverter注解的类,低版本需手动注册):
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import java.util.List; @Configuration public class MongoConfig { @Bean public MongoCustomConversions mongoCustomConversions() { return new MongoCustomConversions(List.of(new AttributesReadConverter())); } }
注意事项
- 实体类必须提供无参构造函数,否则Spring Data MongoDB无法实例化对象。
- 字段建议设为
private并提供getter/setter,避免包私有字段可能导致的赋值失败问题。
内容的提问来源于stack exchange,提问作者ban_ana
相关产品推荐
相关产品推荐

