Skip to content

Instantly share code, notes, and snippets.

@ComFreek
Last active July 31, 2020 09:01
Show Gist options
  • Save ComFreek/838c4d8a20686dfa2aef109e0f012a66 to your computer and use it in GitHub Desktop.
Save ComFreek/838c4d8a20686dfa2aef109e0f012a66 to your computer and use it in GitHub Desktop.
Debugging tips for the circe JSON library for Scala
  1. add import io.circe.generic.auto._ where you use the auto-derived JSON encoders and decoders (don't import at file-level, but instead on object/class-level!)

    • e.g. in a file Datastructures.scala you declare sealed case Data(x: Int) and in Usage.scala you have

      import io.circe.Json  // needed?
      
      // for .asJson methods to be "added" to classes for which implicit encoders exist
      import io.circe.syntax._ 
      object Usage {
        // here we want to make an implicit encoder exist by the auto derive magic of circe:
        // important: this import must happen inside the Usage object, not above!!!
        import io.circe.generic.auto._
      
        val d: Data = Data(42)
      
        def main(args: Array[String]): Unit = {
          println(d.asJson)
        }
      }
      
  2. Have all your case classes for which you would like auto codecs to be derived sealed!

  3. Do not import io.circe.generic.auto._ and io.circe.generic.extras.auto._ at the same time. For me that led to implicit codecs not being created somehow.

  4. Beware of important import statement being marked as unused and then removed (upon clean-up) by IntelliJ. Example from my code: Imgur

    I recommend annotating with heavy comments.

  5. Debugging:

If you get one of the errors

  • could not find Lazy implicit value of type io.circe.generic.decoding.DerivedEncoder[A] or
  • could not find Lazy implicit value of type io.circe.generic.encoding.DerivedDecoder[A],

this means the implicit encoder/decoder for A cannot be found. If you use (or at least intended to use) the auto derive magic of circe via io.circe.generic.auto._ (or io.circe.generic.extras.auto._), then most probably the auto derivation for the respective encoder/decoder did not kick in. Possible reasons are:

- your case classes are not `sealed`
- your case classes got parameters for which auto codecs cannot be derived, e.g. `sealed case class Data(x: Any)`
- you're importing `io.circe.generic.auto._` and `io.circe.generic.extras.auto._` at the same time.
- you imported one of the above imports at the wrong scope. They introduce implicit values, hence they need to be imported in a scope that a) allows declaration of implicit variables (e.g. inside an `object ... { ... }` or class, *not* file scope) and b) is a parent scope of the scope where you want to access the auto-derived codecs.

Manual Pinpointing: (due to this StackOverflow comment) If the tips above don't help you, you can try pinpointing the problem as follows: say you have sealed case class Data(x: Any) and sealed case class Container(d: Data) and you want to auto-derive a codec for Container:

import io.circe.{Encoder, Decoder}

sealed case class Data(x: Any)
sealed case class Container(label: String, d: Data)

object DebuggingTips {
  import io.circe.generic.auto._
  
  // this line will *not* compile
  // the error will be "could not find Lazy implicit value of type io.circe.generic.decoding.DerivedEncoder[Container]"
  val containerEnc = io.circe.Encoder[Container]
}

Then manually look at the parameter types of Container and see if for them, a codec can be automatically derived:

import io.circe.{Encoder, Decoder}

sealed case class Data(x: Any)
sealed case class Container(label: String, d: Data)

object DebuggingTips {
  import io.circe.generic.auto._
  
  // no error here => label parameter is apparently fine!
  val labelEnc = io.circe.Encoder[String]
  
  // error here! Namely "could not find Lazy implicit value of type io.circe.generic.decoding.DerivedEncoder[Data]"
  // => aha!
  //    the reason why a codec for Container cannot be auto-derived is most probably
  //    due to the reason why a codec for Data cannot be auto-derived
  val dataEnc = io.circe.Encoder[Data]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment