Кодирование 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)
Другие вопросы по тегам