0

I'm trying to write a Circe encoder for an object which has a field of scala.collection.immutable.SortedMultiDict. Circe can't find an encoder instance for that, so I need to write one.

import io.circe.{Decoder, Encoder, HCursor}
import io.circe.generic.semiauto._
import io.circe.parser.decode
import scala.collection.immutable.SortedMultiDict
import io.circe.syntax._


implicit val mapEncoder: Encoder[List[(Long, String)]] = deriveEncoder[List[(Long, String)]]
implicit val mapDecoder: Decoder[List[(Long, String)]] = deriveDecoder[List[(Long, String)]]
implicit val oneEncoder: Encoder[SortedMultiDict[Long, String]] = (a: SortedMultiDict[Long, String]) => 
  mapEncoder(a.toList)
implicit val oneDecoder: Decoder[SortedMultiDict[Long, String]] = (c: HCursor) => 
  mapDecoder.map(SortedMultiDict.from[Long, String])(c)

Sadly, this isn't correct...

val test = SortedMultiDict.from[Long, String](Seq(1666268475626L -> "a5d9f51d-35c7-4fef-b4a3-3d28944eeb2b", 1666268475626L -> "df359396-043c-4b65-bc3 -bf309d433ff5"))
val encodedData = test.asJson.noSpaces
val roundTrip = decode[SortedMultiDict[Long, String]](encodedData)

results in

scala> roundTrip
val res2: Either[io.circe.Error,scala.collection.immutable.SortedMultiDict[Long,String]] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(head), DownField(::))))

In fact, the derived list encoder doesn't appear to work...

scala> val myList = List((1666268475626L, "a5d9f51d-35c7-4fef-b4a3-3d28944eeb2b"), (1666268475626L, "df359396-043c-4b65-bc3 -bf309d433ff5"))
val myList: List[(Long, String)] = List((1666268475626,a5d9f51d-35c7-4fef-b4a3-3d28944eeb2b), (1666268475626,df359396-043c-4b65-bc3 -bf309d433ff5))

scala> decode[List[(Long, String)]](myList.asJson.noSpaces)
val res0: Either[io.circe.Error,List[(Long, String)]] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(head), DownField(::))))

Are my expectations of how to do the round trip of encoding/decoding wrong? It's what I'd understood from Circe's codec docs.

EDIT: Well, it works if I change the map codecs to be:

  implicit val mapEncoder: Encoder[List[(Long, String)]] = Encoder.encodeList[(Long, String)]
  implicit val mapDecoder: Decoder[List[(Long, String)]] = Decoder.decodeList[(Long, String)]

I still don't really understand why the earlier ones don't work, though, explications welcome...

user3468054
  • 610
  • 4
  • 11
  • 1
    `derive` is for case classes, and the way `List` are encoded / decoded is very different as how case classes are treated. BTW, you should not need all that, rather just do: `implicit val oneEncoder: Encoder[SortedMultiDict[Long, String]] = Encoder[List[(Long, String)].contrMap(d => dict.toList)` and similar for the `Decoder` but using `map` – Luis Miguel Mejía Suárez Oct 20 '22 at 16:25

0 Answers0