Search code examples
scalacirce

scala circe encoders/decoders for an abstract class with case classes


I want to save a collection of FieldMapping classes as a json string -

abstract class Field {
  def clazz: Class[_]

  def name: String
}

case class StringField(name: String) extends Field {
  override def clazz: Class[_] = classOf[String]
}

case class DateField(name: String) extends Field {
  override def clazz: Class[_] = classOf[Date]
}

... etc - full code here: https://github.com/alexeyOnGitHub/scala-typesafe/blob/master/src/main/scala/com/example/model/Field.scala

Circe code:

import com.example.model.{DateField, Field, FieldMapping, StringField}
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}

object CirceBoilerplateForConfigs {
  implicit val fieldDecoder: Decoder[StringField] = deriveDecoder[StringField]
  implicit val fieldEncoder: Encoder[StringField] = deriveEncoder[StringField]

  implicit val dateDecoder: Decoder[DateField] = deriveDecoder[DateField]
  implicit val dateEncoder: Encoder[DateField] = deriveEncoder[DateField]

  implicit val fooDecoder: Decoder[FieldMapping] = deriveDecoder[FieldMapping]
  implicit val fooEncoder: Encoder[FieldMapping] = deriveEncoder[FieldMapping]
}

Error:(14, 65) could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[com.example.model.FieldMapping] implicit val fooDecoder: Decoder[FieldMapping] = deriveDecoder[FieldMapping] Error:(14, 65)

not enough arguments for method deriveDecoder: (implicit decode: shapeless.Lazy[io.circe.generic.decoding.DerivedDecoder[com.example.model.FieldMapping]])io.circe.Decoder[com.example.model.FieldMapping]. Unspecified value parameter decode. implicit val fooDecoder: Decoder[FieldMapping] = deriveDecoder[FieldMapping] Error:(15, 65)

could not find Lazy implicit value of type io.circe.generic.encoding.DerivedObjectEncoder[com.example.model.FieldMapping] implicit val fooEncoder: Encoder[FieldMapping] = deriveEncoder[FieldMapping] Error:(15, 65)

not enough arguments for method deriveEncoder: (implicit encode: shapeless.Lazy[io.circe.generic.encoding.DerivedObjectEncoder[com.example.model.FieldMapping]])io.circe.ObjectEncoder[com.example.model.FieldMapping]. Unspecified value parameter encode. implicit val fooEncoder: Encoder[FieldMapping] = deriveEncoder[FieldMapping]


Solution

  • Field should be a sealed trait (with abstract class or not sealed trait this won't work).

    The following code compiles:

    import java.util.Date
    
    sealed trait Field {
      def clazz: Class[_]
    
      def name: String
    }
    
    case class StringField(name: String) extends Field {
      override def clazz: Class[_] = classOf[String]
    }
    
    case class DateField(name: String) extends Field {
      override def clazz: Class[_] = classOf[Date]
    }
    
    case class FieldMapping(fieldInConnector1: Option[Field],
                            fieldInConnector2: Option[Field],
                            selected: Boolean,
                            defaultValue: String)
    
    import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
    import io.circe.{Decoder, Encoder}
    object CirceBoilerplateForConfigs {
      implicit val stringDecoder: Decoder[StringField] = deriveDecoder[StringField]
      implicit val stringEncoder: Encoder[StringField] = deriveEncoder[StringField]
    
      implicit val dateDecoder: Decoder[DateField] = deriveDecoder[DateField]
      implicit val dateEncoder: Encoder[DateField] = deriveEncoder[DateField]
    
      implicit val fieldDecoder: Decoder[Field] = deriveDecoder[Field]
      implicit val fieldEncoder: Encoder[Field] = deriveEncoder[Field]
    
      implicit val fooDecoder: Decoder[FieldMapping] = deriveDecoder[FieldMapping]
      implicit val fooEncoder: Encoder[FieldMapping] = deriveEncoder[FieldMapping]
    }