Кодирование рекурсивной структуры данных в Json с помощью Circe при работе на http4s
Я строю очень простой сервис, который должен возвращать древовидную структуру, определенную через рекурсивный класс case:
case class Node(id: Int, name: String, children: Seq[Node] = Seq())
Но по какой-то причине я продолжаю получать следующую ошибку компиляции:
Ошибка:(24, 70) не удалось найти неявное значение для кодировщика параметров: io.circe.Encoder[Seq[com.ansarada.ds.docviewer.server.Main.Node]]
неявный val nodeEncoder: EntityEncoder[Seq[Node]] = jsonEncoderOf[Seq[Node]]Ошибка:(24, 70) недостаточно аргументов для метода jsonEncoderOf: (неявный кодировщик: io.circe.Encoder[Seq[com.ansarada.ds.docviewer.server.Main.Node]])org.http4s.EntityEncoder[Seq[com.ansarada.ds.docviewer.server.Main.Node]]. Не указано значение параметра-энкодера. неявный val nodeEncoder: EntityEncoder[Seq[Node]] = jsonEncoderOf[Seq[Node]]
Код компилируется, как только я удаляю определение дочернего элемента и превращаю Node в плоский объект:
case class Node(id: Int, name: String)
Может кто-нибудь помочь мне определить правильный кодер Json для случая с вложенными детьми?
Полный код:
import org.http4s.circe._
import org.http4s.dsl._
import org.http4s.server.blaze.BlazeBuilder
import org.http4s.server.{Server, ServerApp}
import org.http4s.{EntityEncoder, HttpService}
import scalaz.concurrent.Task
object Main extends ServerApp {
import io.circe.generic.auto._
import io.circe.syntax._
case class Node(id: Int, name: String, children: Seq[Node] = Seq())
def getNodes: Seq[Node] = Seq(
Node(0, "#One"),
Node(1, "#Two"),
Node(2, "#Three")
)
implicit val nodeEncoder: EntityEncoder[Node] = jsonEncoderOf[Node]
implicit val nodesEncoder: EntityEncoder[Seq[Node]] = jsonEncoderOf[Seq[Node]]
override def server(args: List[String]): Task[Server] = {
val nodesService = HttpService {
case _ @ GET -> Root / "nodes" =>
Ok(getNodes.asJson)
}
BlazeBuilder
.bindHttp(8080, "localhost")
.mountService(nodesService, "/api")
.start
}
}
1 ответ
Хорошо. Получил работу, используя аннотации Circe JSON - @JsonCodec. Пошаговое руководство по устранению проблемы, описанной выше:
Предварительные условия - импорт circe lib:
"io.circe" %% "circe-core" % "0.8.0", "io.circe" %% "circe-generic" % "0.8.0", "io.circe" %% "circe-literal" % "0.8.0", "io.circe" %% "circe-parser" % "0.8.0",
Включите плагины автокомпилятора в свой build.sbt:
autoCompilerPlugins := true
Добавьте плагин компилятора райского скаламакроса:
lazy val root = Project("root", file(".")) .settings( addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.patch) )
Аннотируйте классы дел с помощью @JsonCodec:
@JsonCodec case class Node(id: Int, children: Seq[Node] = Seq())
В ролях ответ на JSON:
Ok(getNodes.asJson)
Полный фрагмент кода:
import org.http4s.circe._
import org.http4s.dsl._
import org.http4s.server.blaze.BlazeBuilder
import org.http4s.server.{Server, ServerApp}
import org.http4s.{EntityEncoder, HttpService}
import scalaz.concurrent.Task
object Main extends ServerApp {
import io.circe.generic.auto._
import io.circe.syntax._
@JsonCodec case class Node(id: Int, name: String, children: Seq[Node] = Seq())
def getNodes: Seq[Node] = Seq(
Node(0, "#One"),
Node(1, "#Two"),
Node(2, "#Three")
)
override def server(args: List[String]): Task[Server] = {
val nodesService = HttpService {
case _ @ GET -> Root / "nodes" =>
Ok(getNodes.asJson)
}
BlazeBuilder
.bindHttp(8080, "localhost")
.mountService(nodesService, "/api")
.start
}
}