2

We are using Scalatra 2.4 with JacksonJsonSupport 3.3 and are having problems serializing Java8 LocalDateTime. It is failing with "No constructor for type LocalDateTime, JObject(List())"

I've tried a bunch of searches to find a resolution; with many references to using the JSR-310 ObjectMapper. However, I have not found a way to use this mapper with Scalatra.

Could someone give a pointer on how to serialize Java8 datetime formats with Scalatra's Json support?

Thanks.

Here is what I hope is the relevant sections of the current code. I've included the swagger stuff just in case it is relevant. If something else is needed let me know.

case class EchoPayload(
  message: String,
  author: String,
  createdAt: LocalDateTime
)

class EchoController(val dataSource: EchoDataSource) {
  def findById(echoId: Int): Try[EchoPayload] = {
    dataSource.findById(echoId).map { echo =>
      EchoPayload(echo.message, echo.author.getOrElse("anon"), echo.createdAt)
    }
  }
}

trait BaseRoute extends ScalatraServlet with JacksonJsonSupport with BaseSwagger {
  override protected implicit val jsonFormats = DefaultFormats.withBigDecimal

  before() {
    contentType = formats("json")
  }

  // Error handler, errors translated to Scalatra error ActionResults
  error {
    case bre: BadRequestException => BadRequest(ErrorPayload(bre))
    case nfe: NotFoundException => NotFound(ErrorPayload(nfe))
    case he: HttpException => InternalServerError(ErrorPayload(he))
    case NonFatal(e) => InternalServerError(ErrorPayload(e))
  }

}

class EchoRoute(val controller: EchoController) extends BaseRoute {

  val getEcho = apiOperation[EchoPayload]("getEcho")
    .summary("echo echo echo")
    .notes("returns an echo")
    .parameters(
      pathParam[Int]("echoId").description("id of the echo to return")
    )
    .responseMessages(
      messageOk("success"),
      messageBadRequest("invalid echo id"),
      messageNotFound("no echo yet"))

  get("/:echoId", operation(getEcho)) {
    val echoId = Try(params("echoId").toInt).getOrElse(throw new BadRequestException("echoId is required int"))

    controller.findById(echoId) match {
      case Success(payload) => Ok(payload)
      case Failure(e) if e.isInstanceOf[NoEchoException] => throw new NotFoundException(s"No echo at id=$echoId", e)
      case Failure(e) => throw e
    }
  }
}
John Dilley
  • 123
  • 8

2 Answers2

1

You could add a custom serializer. Adding the following to your scope has worked for me:

 object LocalDateTimeSerializer extends CustomSerializer[LocalDateTime](format => (
{
  case JString(d) => LocalDateTime.parse(d, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
  case JNull => null
},
{
  case x: LocalDateTime => JString(x.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
}
))

implicit val formats = Serialization.formats(NoTypeHints) + LocalDateTimeSerializer`
Germán Bouzas
  • 1,430
  • 1
  • 13
  • 18
0

It looks like you are using json4s for the formatting. Have you looked at how they support extensions? They don't appear to support Java8's java.time directly, but it should be pretty similar to Joda.

Brian Kent
  • 3,754
  • 1
  • 26
  • 31