Circe — использовать поля по умолчанию в классе case при декодировании/кодировании json

Допустим, у меня есть этот класс case:

case class Foo(bar: String, baz: Boolean = false)

который используется при декодировании/кодировании запросов/ответов API с использованием https://github.com/hseeberger/akka-http-json

в примере, похожем на это:

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives
import akka.stream.{ ActorMaterializer, Materializer }
import scala.io.StdIn

object ExampleApp {

  private final case class Foo(bar: String, baz: Boolean = false)

  def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem()
    implicit val mat    = ActorMaterializer()

    Http().bindAndHandle(route, "127.0.0.1", 8000)

    StdIn.readLine("Hit ENTER to exit")
    system.terminate()
  }

  private def route(implicit mat: Materializer) = {
    import Directives._
    import FailFastCirceSupport._
    import io.circe.generic.auto._

    pathSingleSlash {
      post {
        entity(as[Foo]) { foo =>
          complete {
            foo
          }
        }
      }
    }
  }
}

Это работает нормально, если сообщение json включает поле baz. Однако я хочу иметь возможность отправить сообщение json {bar: "something"} и позволить результату использовать значение по умолчанию Foo для baz. Есть ли какая-либо конфигурация в circe или akka-http-json, которая могла бы заставить это работать?

Кроме того, было бы неплохо игнорировать поле baz при повторном кодировании в json, но это не так важно.

Изменить:

Я знаю, что могу сделать что-то вроде этого:

implicit val fooEncoder: Encoder[Foo] = new Encoder[Foo] {
    final def apply(a: Foo): Json = Json.obj(
      ("id", Json.fromString(a.bar))
    )
  }

implicit val fooDecoder: Decoder[Foo] = new Decoder[Decoder] {
  final def apply(c: HCursor): Decoder.Result[Decoder] =
    for {
      bar <- c.downField("bar").as[String]
    } yield {
      Foo(bar)
    }
}

но надеялся на более простое в обслуживании решение, решившее общий случай, когда не требуются поля по умолчанию в сообщении json.


person simen-andresen    schedule 20.11.2017    source источник
comment
Будет ли использование Option[Boolean] допустимым обходным путем? И устанавливать его на None, когда он не нужен?   -  person Yuval Itzchakov    schedule 20.11.2017
comment
@YuvalItzchakov это хорошее предложение, но в этом случае мы используем класс case также с quill. Поскольку поле по умолчанию должно быть NOT NULL в нашей базе данных, было бы неплохо сохранить его как неопциональное в классе case.   -  person simen-andresen    schedule 20.11.2017
comment
@simen-andresen Вам следует рассмотреть возможность принятия приведенного ниже решения.   -  person Lukasz    schedule 21.10.2019


Ответы (1)


Вы можете сделать это с помощью пакета circe-generic-extras. Это отдельная зависимость, которую вы должны добавить в свою сборку. Для сбт это:

libraryDependencies += "io.circe" %% "circe-generic-extras" % "0.8.0"

Затем в вашей функции маршрута замените

import io.circe.generic.extras.Configuration
import io.circe.generic.auto._

с:

import io.circe.generic.extras.auto._
implicit val customConfig: Configuration = Configuration.default.withDefaults

Генерируемые кодировщики всегда будут включать поля по умолчанию.

Для получения дополнительной информации см. примечания к выпуску circe по адресу: https://github.com/circe/circe/releases/tag/v0.6.0-RC1

person Dave DeCaprio    schedule 05.12.2017