Circeのエンコードでちょっとハマって時間を溶かしたので備忘録として書いておきます。
やりたいこと
あるクラスにOption型のフィールドが含まれていて、そのフィールドが存在しない(Noneである)場合はエンコード時にそのフィールドをnullではなくフィールドごと削除したい。
可能であれば余計な記述は少なくしたい。
{ "id" : 1, "name" : "ryskit" "age": null ← 値がない場合はこのフィールドを削除したい }
解決策
その1
以下のように オブジェクト Response
内で io.circe.Encode トレイトを実装する。
これでも実現できますが、わざわざ書くのが面倒です。
package app import io.circe.{Encoder, Json} import io.circe.generic.semiauto.deriveEncoder import io.circe.syntax._ case class Response(id: Int, name: String, age: Option[Int]) object Response { implicit val encoder: Encoder[Response] = new Encoder[Response] { private val generated = deriveEncoder[Response] override def apply(a: Response): Json = generated(a).mapObject(_.filter { case (_, v) => !v.isNull }) } } object Main extends App { val response = Response(1, "ryskit", None) val responseJson = response.asJson.spaces2 println(responseJson) }
その2 (最終的な解決策)
その1と同じように、オブジェクト Response
内で encoderを定義します。
Encoderを実装せずにio.circe.generic.semiauto.deriveEncoderを使用し、dropNullValuesでフィールドを削除します。
これで記述量も少なくなります。
package app import io.circe.Encoder import io.circe.generic.semiauto.deriveEncoder import io.circe.syntax._ case class Response(id: Int, name: String, age: Option[Int]) object Response { implicit val encoder: Encoder[Response] = deriveEncoder[Response].mapJson(_.dropNullValues) } object Main extends App { val response = Response(1, "ryskit", None) val responseJson = response.asJson.spaces2 println(responseJson) }