Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using multiple Writes with Play JSON to render different views of an object

I am writing a Play 2.3 application that serves JSON backed by mongodb. Some of the documents coming from the database contain sensitive fields. I want to be able to work with these documents server side but send a limited JSON view to clients. The documentation on Reads/Writes combinators discusses defining implicit Reads and Writes which works fine for sending data to and from the database but does not completely fill my needs.

What I want to do is define any number of additional Writes that I can use like json "views" to send particular transformations or subsets of models to clients. In Rails I use JBuilder for this purpose.

Trying to explicitly pass a different Writes to toJson does not give me the expected behavior. Take this simple controller action which should write a JSON array of all user ids and usernames:

def listUsers = Action.async {
    val testCustomWrite: Writes[User] = (
      (__ \ "id").write[String] and
      (__ \ "username").write[String]) { user: User =>
        (user._id.toString(), user.username)
      }

    UserDao.findAll().map {
      case Nil => Ok(Json.toJson(""))
      case users => Ok(Json.toJson(users)(testCustomWrite))
    }
  }

This fails to compile with

type mismatch;
[error]  found   : play.api.libs.json.Writes[models.User]
[error]  required: play.api.libs.json.Writes[List[models.User]]
[error]       case users => Ok(Json.toJson(users)(testCustomWrite))

The way toJson handles writing a list of objects depends on an implicit writer for arrays which depends on an implicit writer for a type. I could rewrite the above to to be val testCustomWrite: Writes[List[User]] but that does not feel like the correct solution because Play already provides an array wrapper for implicit writes.

Is there a preferred way to render multiple JSON "views" in Play2?

like image 708
imagio Avatar asked Nov 21 '14 01:11

imagio


1 Answers

When passing the Writes explicitly, the type has to match. In this case you can use Writes.list:

Json.toJson(users)(Writes.list(testCustomWrite))
like image 67
Michael Zajac Avatar answered Sep 18 '22 18:09

Michael Zajac