Search code examples
jsonscalaencodingscala-catscirce

circe encoding putting :: when a list of case class from trait is not sealed


Hello everyone I am facing a problem with the circe library to translate between json and at scala case class any help will be highly appreciate.

in the past i have an ADT like this.

sealed trait Sons

case class Son1(name: String, belongings: List[String]) extends Sons
case class Son2(lastName: String, belongings: List[String]) extends Sons

and have no problems whatsoever but now because of the design the file in witch this trait is, is costly to change we have to remove the sealed from the trait so now we have this (with the sons in different files/libraries)

trait Sons

case class Son1(name: String, belongings: List[String]) extends Sons
case class Son2(lastName: String, belongings: List[String]) extends Sons

when trying to convert a list of sons from Scala to json the library puts :: before the list generating a problem as can be seen in this example.

object Test extends App{


implicit val encoderSon1: Encoder[Son1]=deriveEncoder[Son1]
  implicit val decoderSon1: Decoder[Son1]=deriveDecoder[Son1]
  implicit val encoderSon2: Encoder[Son2]=deriveEncoder[Son2]
  implicit val decoderSon2: Decoder[Son2]=deriveDecoder[Son2]

  implicit val encoderSon: Encoder[Sons] = Encoder.instance {
    case son1: Son1 => son1.asJson
    case son2: Son2 => son2.asJson
  }

  implicit val DecoderSon: Decoder[Sons] =
    List[Decoder[Sons]](
      Decoder[Son1].widen,
      Decoder[Son2].widen
    ).reduceLeft(_ or _)

  implicit val encoderSonList: Encoder[List[Sons]] = deriveEncoder
  implicit val DecoderSonList: Decoder[List[Sons]] = deriveDecoder

  val sonsList: List[Sons] = List(Son1("James",List()),Son2("Mike",List()))
  println(s"1->${sonsList}")
  println(s"2->${sonsList.asJson.noSpaces}")
  val andBack=decode[List[Sons]](sonsList.asJson.noSpaces).fold(fa=>fa.getMessage,fb=>fb.toString())
  println(s"3->${andBack}")
}

the prints are printint

1->List(Son1(James,List()), Son2(Mike,List()))
2->{"::":[{"name":"James","belongings":[]},{"lastName":"Mike","belongings":[]}]}
3->Attempt to decode value on failed cursor: DownField(head),DownField(::)

the problem is of course i do not want the list to be put in a field called :: when converting to JSON.

thank you for any help possible :D


Solution

  • Don't use semi auto for List - semiauto uses slightly different mechanism than other derivations to e.g. avoid issues like:

    implicit val x: X = implicitly[X] // uses val x, so we have cyclic dependency = error in runtime
    

    For that reason when you are using semiauto here, List instances from Encoder and Decoder are ignored, and instead macro generates code for List ADT - that is case class :: and case object Nil.

    If you remove

    
      implicit val encoderSonList: Encoder[List[Sons]] = deriveEncoder
      implicit val DecoderSonList: Decoder[List[Sons]] = deriveDecoder
    

    it will work again.