Кодирование Scala None в значение JSON с использованием circe
Предположим, у меня есть следующие классы case, которые нужно сериализовать как объекты JSON с использованием circe:
@JsonCodec
case class A(a1: String, a2: Option[String])
@JsonCodec
case class B(b1: Option[A], b2: Option[A], b3: Int)
Теперь мне нужно кодировать val b = B(None, Some(A("a", Some("aa")), 5)
как JSON, но я хочу иметь возможность контролировать, выводится ли он как
{
"b1": null,
"b2": {
"a1": "a",
"a2": "aa"
},
"b3": 5
}
или же
{
"b2": {
"a1": "a",
"a2": "aa"
},
"b3": 5
}
С помощью Printer
"s dropNullKeys
конфиг, например b.asJson.noSpaces.copy(dropNullKeys = true)
приведет к опущению None
s из вывода, тогда как установка его в false
будет кодировать None
как null
( см. также этот вопрос). Но как можно контролировать этот параметр для каждого поля?
1 ответ
Лучший способ сделать это, вероятно, просто добавить шаг постобработки в полуавтоматически полученный кодировщик для B
:
import io.circe.{ Decoder, JsonObject, ObjectEncoder }
import io.circe.generic.JsonCodec
import io.circe.generic.semiauto.{ deriveDecoder, deriveEncoder }
@JsonCodec
case class A(a1: String, a2: Option[String])
case class B(b1: Option[A], b2: Option[A], b3: Int)
object B {
implicit val decodeB: Decoder[B] = deriveDecoder[B]
implicit val encodeB: ObjectEncoder[B] = deriveEncoder[B].mapJsonObject(
_.filter {
case ("b1", value) => !value.isNull
case _ => true
}
)
}
А потом:
scala> import io.circe.syntax._
import io.circe.syntax._
scala> B(None, None, 1).asJson.noSpaces
res0: String = {"b2":null,"b3":1}
Вы можете настроить аргумент для фильтра, чтобы удалить все поля с нулевыми значениями, которые вы хотите из объекта JSON (здесь я просто удаляю b1
в B
).
Стоит отметить, что в настоящее время вы не можете объединить @JsonCodec
аннотация и явно определенный экземпляр в сопутствующем объекте. Это не является внутренним ограничением аннотации - мы могли бы проверить объект-компаньон на "переопределение" экземпляров во время расширения макроса, но это сделало бы реализацию значительно более сложной (сейчас это довольно просто). Обходной путь довольно прост (просто используйте deriveDecoder
явно), но, конечно, мы будем рады рассмотреть вопрос о поддержке смешивания и сопоставления @JsonCodec
и явные случаи.
Цирцея добавила метод dropNullValues
на Json
это использует то, что Трэвис Браун упомянул выше.
def dropNulls[A](encoder: Encoder[A]): Encoder[A] =
encoder.mapJson(_.dropNullValues)
implicit val entityEncoder: Encoder[Entity] = dropNulls(deriveEncoder)