MongoDB插入含Rubric为Key的HashMap时出现Invalid BSON字段名错误
MongoDB 的 BSON 文档要求字段名必须为合法字符串,不支持直接将自定义类对象作为 Map 的 key 进行序列化:
你当前定义的rubricDocumentMap是Map<Rubric, List<NewsletterDocument>>类型,使用Rubric实例作为key,MongoDB 驱动序列化时会默认调用Rubric类的toString()方法生成字段名,输出结果就是报错信息里的com.oxway.report.models.Rubric@188993df,这类字符串包含.特殊字符,不符合 MongoDB 字段名规则,同时就算重写toString()规避了格式问题,反序列化时也无法将字符串key自动还原为Rubric实例,本质属于存储结构设计不符合MongoDB的序列化要求。
方案1:修改Map的key类型(最推荐,改造成本最低)
将rubricDocumentMap的key修改为Rubric的唯一标识字段id,类型改为Map<String, List<NewsletterDocument>>:
// 原字段定义修改为 protected Map<String, List<NewsletterDocument>> rubricDocumentMap;
存值时使用Rubric对象的id作为key:
Rubric rubric = new Rubric(); instance.getRubricDocumentMap().put(rubric.getId(), new ArrayList<NewsletterDocument>());
如果业务逻辑需要获取完整的Rubric信息,可以额外在NewsletterInstance类中新增List<Rubric> rubrics字段存储所有关联的Rubric实例,需要匹配时通过id关联即可。
方案2:自定义序列化/反序列化转换器
如果必须保留Map<Rubric, List<NewsletterDocument>>的结构,可针对该字段自定义序列化规则,比如用Spring Data MongoDB的话可以实现Converter接口:
- 序列化阶段:将
Map<Rubric, List<NewsletterDocument>>转换为BSON数组,每个元素包含rubric和documents两个字段 - 反序列化阶段:将BSON数组重新组装为
Map<Rubric, List<NewsletterDocument>>结构
该方案不需要调整业务层代码,但需要适配你当前使用的MongoDB ORM框架,改造成本稍高。
方案3:调整存储结构为嵌套列表
直接放弃Map结构,定义嵌套POJO存储关联关系:
// 新增内部类 public static class RubricWithDocuments { private Rubric rubric; private List<NewsletterDocument> documents; // 省略getter、setter } // 原rubricDocumentMap字段修改为 protected List<RubricWithDocuments> rubricDocumentList;
该结构完全符合MongoDB的文档序列化规则,查询和修改都更方便,适合业务逻辑可以调整存储结构的场景。
内容的提问来源于stack exchange,提问作者user15500784

