You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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结构:

  1. 实现读转换器(将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;
    }
}
  1. 在实体类中指定使用该转换器:
@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; }
}
  1. 注册转换器(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:34:54