scalaplayframework-2.0scala-implicits

Play not finding implicit definition


I am writing a JSON Writes. In models/Users.scala, I have defined an implicit object with implicit definitions.

object UserImplicits {

  /*Writes (write to JsValue) are used by toJson method of Json object to convert data (say the model) to JsValue*/
    implicit val profileWrites:Writes[UserProfile] = (
        (JsPath \ "confirmed").write[Boolean] and
          (JsPath \ "email").write[String] and
          (JsPath \ "firstname").write[String] and
          (JsPath \ "lastname").write[String]
        ) (unlift(UserProfile.unapply))

      implicit val userWrites: Writes[User] = (
        (JsPath \ "id").write[UUID] and
          (JsPath \ "user-profile").write[UserProfile]
        ) (unlift(User.unapply))

      implicit val usersResourceWrites:Writes[UsersResource] = (
        (JsPath \ "id").write[String] and
          (JsPath \ "url").write[String] and
          (JsPath \ "user").write[User]
        ) (unlift(UsersResource.unapply))


  /*Reads (read from JsValue) is used by Json object's as or asOpt methods to convert JsValue to some other data, eg your model*/

      implicit val profileReads:Reads[UserProfile] = (
        (JsPath \ "confirmed").read[Boolean] and
          (JsPath \ "email").read[String] and
          (JsPath \ "firstname").read[String] and
          (JsPath \ "lastname").read[String]
        ) (UserProfile.apply _)

      implicit val userReads: Reads[User] = (
        (JsPath \ "id").read[UUID] and
          (JsPath \ "user-profile").read[UserProfile]
        ) (User.apply _)

      implicit val usersResourceReads: Reads[UsersResource] = (
        (JsPath \ "id").read[String] and
          (JsPath \ "url").read[String] and
          (JsPath \ "user").read[User]
        ) (UsersResource.apply _)

}

In my controller class, I have imported models._ and have defined the controller as follows:

import models._

import scala.concurrent.{ExecutionContext, Future}

class UserController @Inject()(cc: ControllerComponents)(implicit exec: ExecutionContext) extends AbstractController(cc){

  //TODOM - remove hard coded response
  def addUser = Action.async{ implicit request => {
    println("addUser controller called")
    val user = User(UUID.randomUUID(),UserProfile(true,"m@m.com","m","c"))
    val userResource = UsersResource(user.id.toString(),"/ws/users",user)
    val json = Json.toJson(userResource); //converts the model to JsValue using Writes defined in Users model class
    println("returning json:",Json.prettyPrint(json))
    Future{Ok(json)}}
  }

I am getting the following compilation error.

No Json serializer found for type models.UsersResource. Try to implement an implicit Writes or Format for this type. for code val json = Json.toJson(userResource);

The issue seem to be Play cannot find the implicit Writes. The code works if I move the implicit definitions in controller instead of defining in models. How could I make the implicits defined in model/user.scala visible in the controller class?


Solution

  • I had to create another object (not companion object) and add the implicit definitions there.

    to use these implicits, use import models.UserImplicits._ in files where implicits are required.

    object UserImplicits {
    
        /*Writes (write to JsValue) are used by toJson method of Json object to convert data (say the model) to JsValue*/
        implicit val profileWrites:Writes[UserProfile] = (
          (JsPath \ "confirmed").write[Boolean] and
            (JsPath \ "email").writeNullable[String] and
            (JsPath \ "firstname").writeNullable[String] and
            (JsPath \ "lastname").writeNullable[String]
          ) (unlift(UserProfile.unapply))
    
    
        implicit val userWrites: Writes[User] = (
          (JsPath \ "id").write[UUID] and
            (JsPath \ "user-profile").write[UserProfile]
          ) (unlift(User.unapply))
    
       implicit val usersResourceWrites:Writes[UsersResource] = (
          (JsPath \ "id").write[String] and
            (JsPath \ "url").write[String] and
            (JsPath \ "user").write[User]
          ) (unlift(UsersResource.unapply))
    
    
        /*Reads (read from JsValue) is used by Json object's as or asOpt methods to convert JsValue to some other data, eg your model*/
    
      implicit val profileReads:Reads[UserProfile] = (
        (JsPath \ "confirmed").read[Boolean] and
          (JsPath \ "email").readNullable[String] and
          (JsPath \ "firstname").readNullable[String] and
          (JsPath \ "lastname").readNullable[String]
        ) (UserProfile.apply _)
    
    
        implicit val userReads: Reads[User] = (
          (JsPath \ "id").read[UUID] and
            (JsPath \ "user-profile").read[UserProfile]
          ) (User.apply _)
    
        implicit val usersResourceReads: Reads[UsersResource] = (
          (JsPath \ "id").read[String] and
            (JsPath \ "url").read[String] and
            (JsPath \ "user").read[User]
          ) (UsersResource.apply _)
    
    }