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

MongoDB Document JSON转Scala样例类时日期转换异常解决方案

解决方案

别担心,这个问题在Scala操作MongoDB时太常见了!主要是MongoDB存储的日期格式(带$date的时间戳结构)和Scala默认的日期类型不兼容导致的,给你几个实用的解决办法:

1. 用Mongo驱动适配的日期类型定义样例类

MongoDB Scala驱动已经为常见的日期类型提供了自动转换支持,比如org.joda.time.DateTime或者Java 8的java.time.Instant。你可以直接把样例类的日期字段定义成这些类型:

import org.joda.time.DateTime
import org.mongodb.scala.bson.ObjectId

case class WorkOrder(
  _id: ObjectId,
  workOrderId: String,
  thingId: String,
  alertId: String,
  description: String,
  contactId: String,
  estimatedDuration: String,
  workOrderStatus: String,
  lifecycleStatus: String,
  maintenancePlanId: String,
  createdDate: DateTime,
  updatedDate: DateTime,
  lastViewedDate: DateTime,
  suggestedMaintenanceDate: DateTime,
  startDate: DateTime
)

只要你的项目依赖里包含了对应的库(比如Joda Time或者Java 8时间库),驱动会自动处理$date格式到这些类型的转换,不用额外写代码。

2. 自定义编解码器适配你想用的日期类型

如果你的项目更倾向于用Java 8的LocalDateTime或者其他自定义日期类型,可以写一个专属的编解码器(Codec)来处理转换逻辑,然后注册到Mongo客户端:

比如针对java.time.Instant的编解码器示例:

import org.bson.{BsonReader, BsonWriter}
import org.bson.codecs.{Codec, DecoderContext, EncoderContext}
import java.time.Instant

class InstantCodec extends Codec[Instant] {
  override def encode(writer: BsonWriter, value: Instant, encoderContext: EncoderContext): Unit = {
    writer.writeDateTime(value.toEpochMilli)
  }

  override def getEncoderClass: Class[Instant] = classOf[Instant]

  override def decode(reader: BsonReader, decoderContext: DecoderContext): Instant = {
    Instant.ofEpochMilli(reader.readDateTime())
  }
}

然后注册这个编解码器到Mongo客户端配置:

import org.mongodb.scala.MongoClientSettings
import org.bson.codecs.configuration.CodecRegistries.{fromProviders, fromRegistries}
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.mongodb.scala.bson.codecs.Macros._

// 注册编解码器
val codecRegistry = fromRegistries(
  fromProviders(classOf[WorkOrder]), // 样例类的自动编解码器
  fromRegistries(new InstantCodec()), // 自定义的Instant编解码器
  DEFAULT_CODEC_REGISTRY // 默认Mongo编解码器
)

// 配置客户端
val settings = MongoClientSettings.builder()
  .codecRegistry(codecRegistry)
  .build()

val mongoClient = MongoClient(settings)

这样你的样例类里就可以放心用Instant类型来接收日期字段了。

3. 手动转换Document中的日期字段

如果只是临时处理或者不想改动全局配置,也可以在拿到Document后手动提取时间戳,转换成你需要的日期类型:

import java.time.Instant

MongoFactory.WorkOrdercollection.find().subscribe( 
  (doc: Document) => {
    // 手动提取日期时间戳并转换
    val createdDate = Instant.ofEpochMilli(doc.getDateTime("createdDate").getValue)
    val updatedDate = Instant.ofEpochMilli(doc.getDateTime("updatedDate").getValue)
    // 其他日期字段同理

    // 构建样例类实例
    val workOrder = WorkOrder(
      _id = doc.getObjectId("_id"),
      workOrderId = doc.getString("workOrderId"),
      thingId = doc.getString("thingId"),
      alertId = doc.getString("alertId"),
      description = doc.getString("description"),
      contactId = doc.getString("contactId"),
      estimatedDuration = doc.getString("estimatedDuration"),
      workOrderStatus = doc.getString("workOrderStatus"),
      lifecycleStatus = doc.getString("lifecycleStatus"),
      maintenancePlanId = doc.getString("maintenancePlanId"),
      createdDate = createdDate,
      updatedDate = updatedDate,
      lastViewedDate = Instant.ofEpochMilli(doc.getDateTime("lastViewedDate").getValue),
      suggestedMaintenanceDate = Instant.ofEpochMilli(doc.getDateTime("suggestedMaintenanceDate").getValue),
      startDate = Instant.ofEpochMilli(doc.getDateTime("startDate").getValue)
    )
    println(workOrder)
  }, 
  (error: Throwable) => println(s"Query failed: ${error.getMessage}"), 
  () => println("Done") 
)

这种方式虽然代码繁琐一点,但灵活性很高,适合小范围的场景。


内容的提问来源于stack exchange,提问作者Tejas R.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:28:23