sirthias / borer

Efficient CBOR and JSON (de)serialization in Scala
https://sirthias.github.io/borer/
Mozilla Public License 2.0
220 stars 13 forks source link

Derived codecs don't pick up custom codecs for basic types #42

Closed plokhotnyuk closed 5 years ago

plokhotnyuk commented 5 years ago

In previous version it worked fine:

scala> :paste
// Entering paste mode (ctrl-D to finish)

import io.bullet.borer._
import io.bullet.borer.derivation.MapBasedCodecs._

def stringCodec[T](f: String => T): Codec[T] = 
  Codec((w: Writer, value: T) => w.writeString(value.toString), (r: Reader) => f(r.readString()))

implicit val Codec(charEnc: Encoder[Char], charDec: Decoder[Char]) = stringCodec(_.charAt(0))

case class HasChar(ch: Char)

implicit val Codec(hasCharEnc: Encoder[HasChar], hasCharDec: Decoder[HasChar]) = deriveCodec[HasChar]

io.bullet.borer.Json.decode("""{"ch":"F"}""".getBytes).to[HasChar].value

// Exiting paste mode, now interpreting.
...
res0: HasChar = HasChar(F)

Now it tries to parse chars as ints without taking in account a custom codec provided by the implicit and throws the following error:

io.bullet.borer.Borer$Error$InvalidInputData: Expected Char but got Chars (input position 6)
  at io.bullet.borer.InputReader.unexpectedDataItem(Reader.scala:536)
  at io.bullet.borer.InputReader.unexpectedDataItem(Reader.scala:532)
  at io.bullet.borer.InputReader.readChar(Reader.scala:85)
  at HasCharDecoder$1.readObject$1(<console>:1)
  at HasCharDecoder$1.read(<console>:1)
  at $anon$2.read(<console>:1)
  at $anon$2.read(<console>:1)
  at io.bullet.borer.DecodingSetup$Impl.decodeFrom(DecodingSetup.scala:160)
  at io.bullet.borer.DecodingSetup$Impl.value(DecodingSetup.scala:99)
  ... 36 elided

But for some cases like bellow it picks the custom codec:

scala> io.bullet.borer.Json.decode("""["F"]""".getBytes).to[List[Char]].value
...
res1: List[Char] = List(F)
sirthias commented 5 years ago

The reason for this is an optimization that came with 0.10.0. For members with basic types derived codecs now don't rely on the implicit codecs, but read them directly from the reader, which circumvents boxing.

So, for all non-basic types custom codecs are still picked up as before.

sirthias commented 5 years ago

Theoretically this could be fixed by running implicit resolution at the macros call site for all encoders/decoders, including the ones for basic types, and comparing whether they match the default definitions. If they do we can take the shortcut, otherwise we need to go through the custom codecs. However, at this point I'm not sure that there are sufficiently important real use cases for overriding the basic codecs to justify this investment...