Search code examples
jsonscalatraitsargonaut

Implicit encoder for a trait type parameter


I would like to encode to json a field of type List[E] using argonaut lib.

sealed trait Msg[E] {
  val contents: List[E]    

  def send(): Unit = {
    val json = contents.asJson
    println("Sending json: " + json.toString())
  }
}

Then I have a StringMsg case class:

case class StringMsg(contents: List[String]) extends Msg[String]

The argonaut lib defines the JsonIdentity[J] trait:

trait JsonIdentity[J] {
  val j: J

  /**
   * Encode to a JSON value using the given implicit encoder.
   */
  def jencode(implicit e: EncodeJson[J]): Json =
    e(j)
}

When I create a new instance of StringMsg and call the send() method, I have the following error:

StringMsg(List("a","b")).send()

could not find implicit value for parameter e: argonaut.EncodeJson[List[E]]


Solution

  • Your API should require implicit argonaut.EncodeJson[List[E]] from client code:

    sealed trait Msg[E] {
      val contents: List[E]
      implicit def encodeJson: argonaut.EncodeJson[List[E]] //to be implemented in subclass   
    
      def send(): Unit = {
        val json = contents.asJson
        println("Sending json: " + json.toString())
      }
    }
    
    //or
    
    abstract class Msg[E](implicit encodeJson: argonaut.EncodeJson[List[E]]) {
      val contents: List[E]
    
      def send(): Unit = {
        val json = contents.asJson
        println("Sending json: " + json.toString())
      }
    }
    
    //or
    
    sealed trait class Msg[E] {
      val contents: List[E]
    
      def send()(implicit encodeJson: argonaut.EncodeJson[List[E]]): Unit = {
        val json = contents.asJson
        println("Sending json: " + json.toString())
      }
    }
    

    Somewhere in the client-code:

    case class StringMsg(contents: List[String]) extends Msg[String] { 
       implicit val encodeJson = argonaut.StringEncodeJson 
    }
    
    //or
    
    import argonaut.StringEncodeJson //or even import argonaut._
    case class StringMsg(contents: List[String]) extends Msg[String]() //implicit will be passed here
    
    //or
    
    import argonaut.StringEncodeJson //or even import argonaut._
    case class StringMsg(contents: List[String]) extends Msg[String]
    val a = StringMsg(Nil)
    a.send() //implicit will be passed here