2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FinatraのCustom Deserialize

Last updated at Posted at 2017-11-25

リクエストパラメータの型をjava.time.LocalDateTimeにした時にただの型指定ではエラーが出たので自分でdeserializer書かないといけなかった

リクエストcase classのパラメータは以下のような感じ

import com.twitter.finatra.request.FormParam
import java.time.LocalDateTime

case class CustomRequest(
  @FormParam name: String,
  @FormParam date: Option[LocalDateTime] // ここがデフォルトだとdeserialize出来ない
)

CustomDeserializer書かないままAPIにリクエストすると以下のようなレスポンスが返ってきた
リクエストパラメータはdateが2017-01-20 00:00:00

{
	"errors": {
		"date": "Can not construct instance of java.time.LocalDateTime: no String-argument constructor/factory method to deserialize from String value ('2017-01-20 00:00:00')\n at [Source: N/A; line: -1, column: -1]"
	}
}

参照

今回は先に書いておく
日本語情報無くてつらまってきた
Finatra結構使ってるよという方 :thumbsup: とか絵文字でもいいのでコメント下さい…

Finatra公式 Jackson Integration Adding a Custom Serializer or Deserializer
https://twitter.github.io/finatra/user-guide/json/index.html#customization

finatra/jackson/src/main/scala/com/twitter/finatra/json/internal/serde/FinatraSerDeSimpleModule.scala
https://github.com/twitter/finatra/blob/d0c659e3d139b7c3c1c075133c65a9828a00cbb1/jackson/src/main/scala/com/twitter/finatra/json/internal/serde/FinatraSerDeSimpleModule.scala

finatra/jackson/src/test/scala/com/twitter/finatra/json/tests/internal/ExampleCaseClasses.scala
https://github.com/twitter/finatra/blob/master/jackson/src/test/scala/com/twitter/finatra/json/tests/internal/ExampleCaseClasses.scala

Custom JSON Deserialization with Jackson
https://dzone.com/articles/custom-json-deserialization-with-jackson

Getting Started with Custom Deserialization in Jackson
http://www.baeldung.com/jackson-deserialization

解決手順とコード

解決手順は公式に書いてある、だが例がもうちょっと…
なので動く例も載せる

解決手順

  1. 指定の型のdeserializer書く
  2. そのdeserializerをJacksonModulesに追加作成
  3. FinatraのHttpServerのjacksonModuleを作成したJacksonModulesでoverride

以上

以下はCustomJacksonModuleを作成するコード

CustomJacksonModule.scala
package yourpackage.modules

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

import com.fasterxml.jackson.annotation.JsonInclude.Include
import com.fasterxml.jackson.core.JsonGenerator.Feature
import com.fasterxml.jackson.core.{JsonParser, ObjectCodec}
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.{DeserializationContext, JsonNode, ObjectMapper, PropertyNamingStrategy}
import com.fasterxml.jackson.databind.module.SimpleModule
import com.twitter.finatra.json.modules.FinatraJacksonModule


// custom deserializer
class CustomLocalDateTimeDeserializer extends JsonDeserializer[LocalDateTime] { // LocalDateTimeが変換したい型
  override def deserialize(jp: JsonParser, ctxt: DeserializationContext) = {
    import scala.util.control.Exception._

    val oc: ObjectCodec = jp.getCodec()
    val node: JsonNode = oc.readTree(jp)
    val strDate: String = node.asText()

    val parseRes = catching(classOf[java.time.format.DateTimeParseException]) opt LocalDateTime.parse(strDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
    parseRes match {
      case Some(localDateTime) => localDateTime
      case None => null
    }
  }
}

// Jackson SimpleModule for custom deserializer
class CustomDeserializerModule extends SimpleModule {
  addDeserializer(classOf[LocalDateTime], new CustomLocalDateTimeDeserializer)
}

// custom FinatraJacksonModule which replace the framework module
object CustomJacksonModule extends FinatraJacksonModule {
  override val additionalJacksonModules = Seq(
    new CustomDeserializerModule
  )

  override val serializationInclusion = Include.NON_EMPTY

  override val propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE // リクエストパラメータがスネークケース dateOfBirthではなくdate_of_birthじゃないとdeserializeしようとしない

  override def additionalMapperConfiguration(mapper: ObjectMapper) {
    mapper.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true)
  }
}

以下は作成したCustomJacksonModuleでjacksonModuleをoverrideするコード

main.scala
class MainServer extends HttpServer with Logging {
  override def jacksonModule = CustomJacksonModule // ここ書かないと適用されない

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .filter[CommonFilters]
      .add[SomeController]
  }
}

上記を適用すると2017-01-20 00:00:00のようなパラメータをLocalDateTime型で受け取る事ができるようになった
2017-01-20だけだとparseに失敗してdateがOption型なのでNoneになる

以上

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?