在http4s上运行时,将带有Circe的递归数据结构编码到Json中

时间:2017-06-16 11:06:46

标签: json scala circe http4s http4s-circe

我正在构建一个非常简单的服务,它应该返回一个通过递归case类定义的树状结构:

case class Node(id: Int, name: String, children: Seq[Node] = Seq())

但由于某种原因,我不断收到以下编译错误:

  

错误:(24,70)无法找到参数编码器的隐含值:   io.circe.Encoder [序号[com.ansarada.ds.docviewer.server.Main.Node]]
  隐式val nodesEncoder:EntityEncoder [Seq [Node]] =   jsonEncoderOf [Seq [Node]]

     

错误:(24,70)没有足够的参数   方法jsonEncoderOf :(隐式编码器:   io.circe.Encoder [序号[com.ansarada.ds.docviewer.server.Main.Node]])org.http4s.EntityEncoder [序号[com.ansarada.ds.docviewer.server.Main.Node]。   未指定的值参数编码器。隐式val nodesEncoder:   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 个答案:

答案 0 :(得分:1)

确定。使用circe json注释工作 - @JsonCodec。 解决上述问题的分步指南:

  1. 先决条件 - 导入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",
    
  2. 在build.sbt中打开自动编译器插件:

    autoCompilerPlugins := true
    
  3. 添加scalamacros paradise编译器插件:

    lazy val root = Project("root", file("."))
        .settings(
            addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.patch)
    )
    
  4. 使用@JsonCodec注释案例类:

    @JsonCodec case class Node(id: Int, children: Seq[Node] = Seq())
    
  5. 对json投射回复:

    Ok(getNodes.asJson)
    
  6. 完整的代码段:

    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
      }
    }