Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert sealed trait case objects to string using circe

Tags:

scala

circe

I am using Scala and Circe. I have the following sealed trait.

  sealed trait Mode
  case object Authentication extends Mode
  case object Ocr extends Mode

The output of this case object when called SessionModel.Authentication is the following:

"Authentication":{}

I need to convert this to a string so it outputs "authentication"

like image 344
Kay Avatar asked Nov 28 '19 10:11

Kay


Video Answer


1 Answers

As Andriy Plokhotnyuk notes above, you can use circe-generic-extras:

import io.circe.Codec
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto.deriveEnumerationCodec

sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode

object Mode {
  private implicit val config: Configuration =
    Configuration.default.copy(transformConstructorNames = _.toLowerCase)

  implicit val modeCodec: Codec[Mode] = deriveEnumerationCodec[Mode]
}

And then:

scala> import io.circe.syntax._
import io.circe.syntax._

scala> (Authentication: Mode).asJson
res1: io.circe.Json = "authentication"

scala> io.circe.Decoder[Mode].decodeJson(res1)
res2: io.circe.Decoder.Result[Mode] = Right(Authentication)

(Note that Codec is new in 0.12—for earlier versions you'll have to write out both instances as in Andriy's comment.)

Unless you have a lot of these to maintain, though, I personally think writing the instances out by hand is often better than using circe-generic-extras, and in this case it's not even much more verbose:

import io.circe.{Decoder, Encoder}

sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode

object Mode {
  implicit val decodeMode: Decoder[Mode] = Decoder[String].emap {
    case "authentication" => Right(Authentication)
    case "ocr"            => Right(Ocr)
    case other            => Left(s"Invalid mode: $other")
  }

  implicit val encodeMode: Encoder[Mode] = Encoder[String].contramap {
    case Authentication => "authentication"
    case Ocr            => "ocr"
  }
}

Which works exactly the same as the deriveEnumerationCodec version but doesn't require anything but circe-core, is less magical, compiles much faster, etc. Generic derivation can be great for simple case classes with straightforward mappings, but I think people too often try to stretch it to cover all cases when writing instances manually wouldn't be much of a burden and might even be clearer.

like image 167
Travis Brown Avatar answered Sep 28 '22 15:09

Travis Brown