Вы можете сделать это довольно просто, сопоставив фильтр с выходным сигналом кодировщика (который может быть получен с помощью Encoder.forProductN
и т. Д.):
import io.circe.{ Json, ObjectEncoder }
import io.circe.generic.semiauto.deriveEncoder
case class Example(f1: Option[Int], f2: Option[Int])
val keepSomeNulls: ((String, Json)) => Boolean = {
case ("f1", v) => !v.isNull
case (_, _) => true
}
implicit val encodeExample: ObjectEncoder[Example] =
deriveEncoder[Example].mapJsonObject(_.filter(keepSomeNulls))
А потом:
scala> import io.circe.syntax._
import io.circe.syntax._
scala> Example(Some(1), Some(2)).asJson.noSpaces
res0: String = {"f1":1,"f2":2}
scala> Example(Some(1), None).asJson.noSpaces
res1: String = {"f1":1,"f2":null}
scala> Example(None, Some(2)).asJson.noSpaces
res2: String = {"f2":2}
scala> Example(None, None).asJson.noSpaces
res3: String = {"f2":null}
Обратите внимание, что настройка принтера для сбрасывания нулевых значений все равно удалит "f2": null
здесь. Это одна из причин, по которой я думаю, что в целом лучше сделать сохранение нулевых значений исключительно ответственностью принтера, но в таком случае наличие или отсутствие нулевых полей явно семантически значимо, так что должны перепутать уровни.