MongoDB Document映射Scala Case Class遇日期解析问题,求解决方案
如何将MongoDB Document正确映射到Scala Case Class?
我来帮你解决这个MongoDB Scala驱动的映射问题,其实官方驱动已经提供了原生的Case Class映射支持,完全可以跳过手动转JSON再解析的步骤,直接把Mongo的Document映射到你的WorkOrder case class,还能自动处理日期类型的转换。下面是具体的实现步骤:
1. 确保依赖正确
首先,你的项目需要引入MongoDB Scala驱动的依赖(以SBT为例):
libraryDependencies += "org.mongodb.scala" %% "mongodb-scala-driver" % "4.11.1"
这个依赖包含了处理Scala Case Class映射的核心组件。
2. 配置CodecRegistry,开启Case Class自动映射
MongoDB驱动通过CodecRegistry来处理不同类型的编解码,我们需要注册Scala Case Class的编解码器,以及日期类型的转换规则。修改你的MongoDB初始化代码:
import org.mongodb.scala._ import org.mongodb.scala.bson.codecs.MongoScalaCodecProvider import org.bson.codecs.configuration.CodecRegistries.{fromProviders, fromRegistries} import org.bson.codecs.configuration.CodecRegistry import java.util.Date import org.bson.codecs.pojo.annotations.BsonProperty // 定义你的Case Class,注意用@BsonProperty映射Mongo的_id字段 case class WorkOrder ( @BsonProperty("_id") var id : String = (new ObjectId()).toString(), var workOrderId: String, var thingId : String, var alertId : String, var description : String, val lastViewedDate : Date, val suggestedMaintenanceDate : Date, val startDate : Date ) // 配置CodecRegistry:默认驱动编解码器 + Scala Case Class编解码器 + Date类型支持 private val codecRegistry: CodecRegistry = fromRegistries( MongoClientSettings.getDefaultCodecRegistry, fromProviders(MongoScalaCodecProvider()), fromProviders(classOf[Date]) ) private val client: MongoClient = MongoClient() // 给数据库绑定自定义的CodecRegistry private val database: MongoDatabase = client.getDatabase("rulemgntdb").withCodecRegistry(codecRegistry) // 直接获取WorkOrder类型的集合,不再是Document val WorkOrdercollection: MongoCollection[WorkOrder] = database.getCollection("workOrder")
这里的关键细节:
- 用
@BsonProperty("_id")把case class里的id字段映射到Mongo文档中的_id键,解决字段名不匹配的问题 - 通过
MongoScalaCodecProvider自动生成Case Class的编解码器,不需要手动写转换逻辑 - 注册
Date类型的编解码器,驱动会自动把Mongo的BsonDateTime转换成Scala的java.util.Date
3. 直接查询并获取Case Class实例
现在你的查询代码可以直接返回Seq[WorkOrder],不需要再处理Document:
MongoFactory.WorkOrdercollection.find().collect().subscribe( (results: Seq[WorkOrder]) => println(s"Found: #${results}"), (error: Throwable) => println(s"Query failed: ${error.getMessage}"), () => println("Done") )
这样返回的结果就是你定义的WorkOrder实例,日期字段也会被正确转换,完全不需要处理JSON里的$date格式。
备选方案:如果必须转JSON再解析(不推荐)
如果你因为某些原因一定要先把Document转成JSON再解析,可以用Jackson库配合MongoDB的JSON模块来处理日期格式。首先添加依赖:
libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.15.2" libraryDependencies += "org.mongodb" % "mongodb-driver-core" % "4.11.1"
然后用Jackson的ObjectMapper来解析:
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule import org.bson.json.JsonMode import org.bson.json.JsonWriterSettings // 配置Jackson,支持Scala类型和MongoDB日期格式 val mapper = new ObjectMapper() .registerModule(DefaultScalaModule) .configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 查询时转JSON并解析 MongoFactory.WorkOrdercollection.find().subscribe( (doc: Document) => { // 用RELAXED模式输出JSON,避免多余的类型标识 val json = doc.toJson(JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build()) val workOrder = mapper.readValue(json, classOf[WorkOrder]) println(workOrder) }, (error: Throwable) => println(s"Query failed: ${error.getMessage}"), () => println("Done") )
不过这种方法多了一次JSON序列化和反序列化的步骤,性能不如直接用CodecRegistry的方案,所以优先推荐第一种原生映射的方式。
内容的提问来源于stack exchange,提问作者Nilesh
相关产品推荐
相关产品推荐

