0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Scala】ネストされているclassをcirceでencodeする際にビルドエラー

Last updated at Posted at 2021-11-27

概要

circeはScalaで使用できるJSONのパース用ライブラリです。Akka HTTP + circe + Flywayを用いた雑なHTTPサーバのサンプルと解説の記事にある通り、AkkaHTTPで使用されているイメージがあります。
今回はこのcirceで、ネストがあるclassをencodeする時に少しハマったのでメモ書きを残しておきます。

エラー内容

まず、今回JSONにencodeする対象のclassは以下とします。InitialResponseの配下にCompanyResponseのclassがいる構成になっています。

InitialResponse.scala
case class InitialResponse
(
  byNpmrSortCompanies: Seq[CompanyResponse], // 売上高当期純利益率順
  byEquityRatioCompanies: Seq[CompanyResponse], // 純資産比率順
  byRoaCompanies: Seq[CompanyResponse], // ROA順
  byRoeCompanies: Seq[CompanyResponse], // ROE順
)
CompanyResponse.scala
case class CompanyResponse
(
  companyId: String,
  companyName: String,
  address: String, // 住所
  accountingPeriod: String, // 決算期
  salesAmount: BigInt, // 売上高
  netIncome: BigInt, // 当期純利益
  totalAssets: BigInt, // 総資産
  netAssets: BigInt, // 純資産
  employees: BigInt, // 従業員数
  npmr: BigDecimal, // 売上高当期純利益率
  roa: BigDecimal, // ROA
  roe: BigDecimal, // ROE
  equityRatio: BigDecimal, // 純資産比率
  salesPerEmployee: BigInt, // 一人当たり売上高
)

さて、この構成でInitialResponseを以下のようなコードにて、JSONにencodeするとどうなるか。ビルド時にcould not find Lazy implicit value of type io.circe.generic.encoding.DerivedAsObjectEncoderというエラーが発生します。

import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
import akka.http.scaladsl.server.Directives.{complete, failWith}
import akka.http.scaladsl.server.StandardRoute
import companyInfo.model.api.{CompanyResponse, InitialResponse}
import companyInfo.useCase.CompanySearchUseCase
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
import io.circe.syntax._

object CompanySearchController {

  implicit val encodeInitialResponse: Encoder[InitialResponse] = deriveEncoder[InitialResponse]

  def getInitialCompanyFetch(): StandardRoute = {

    // InitialResponseのオブジェクトを取得(内容は割愛)
    CompanySearchUseCase.getInitialDisplayCompanyInfo() match {
      case Left(ex) => failWith(ex)
      // InitialResponse型
      case Right(initialRes) =>
        // 受け取ったオブジェクトをJSONにencode
        val res = initialRes.asJson
        complete(HttpEntity(
          ContentTypes.`application/json`,
          s"$res"
        ))
    }
  }
}

対応

原因は、EncoderにInitialResponseしか設定していないことです。ネストしているclassについては、その子のclassのEncoderも定義する必要があります。
ということで、以下のようにCompanyResponseのEncoderを追加すれば解消します。なお、全ソースコードはこちらにあります。

import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
import akka.http.scaladsl.server.Directives.{complete, failWith}
import akka.http.scaladsl.server.StandardRoute
import companyInfo.model.api.{CompanyResponse, InitialResponse}
import companyInfo.useCase.CompanySearchUseCase
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
import io.circe.syntax._

object CompanySearchController {

  // CompanyResponseのEncoderを追加
  implicit val encodeCompanyResponse: Encoder[CompanyResponse] = deriveEncoder[CompanyResponse]
  implicit val encodeInitialResponse: Encoder[InitialResponse] = deriveEncoder[InitialResponse]

  def getInitialCompanyFetch(): StandardRoute = {

    // InitialResponseのオブジェクトを取得(内容は割愛)
    CompanySearchUseCase.getInitialDisplayCompanyInfo() match {
      case Left(ex) => failWith(ex)
      // InitialResponse型
      case Right(initialRes) =>
        // 受け取ったオブジェクトをJSONにencode
        val res = initialRes.asJson
        complete(HttpEntity(
          ContentTypes.`application/json`,
          s"$res"
        ))
    }
  }
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?