[2.0] Scala Json Generics & creating a document with Reactive Mongo

686 views
Skip to first unread message

Alex Jarvis

unread,
Sep 25, 2012, 6:59:15 AM9/25/12
to play-fr...@googlegroups.com
Surely there has to be an easier way?

I've just read through the documentation here: http://www.playframework.org/documentation/2.0.3/ScalaJsonGenerics

and although useful, what I really want to do is just define my 'case class' and have Play automagically map the Json to this object, throwing a validation error if it does not match the attributes etc.

Has anyone else thought about this?

What I am trying to achieve is a nice way to insert a mongodb document with Reactive Mongo. I've had a look at the examples and they seem to just pass in a JSValue straight from the request body using parse.json. This is not really desirable as I want to validate the Json sent from the client before blindly inserting a document with it.

I see that I can define an implicit BSONWriter for my case class and also define a reads method as mentioned in the Scala Json Generics documentation, but this means that I'll then be providing custom boilerplate code to map from my Json -> Scala objects -> BSON

Maybe I just need a neat way of validating the Json that has already been serialised for me using the parse.json body parser?

Any help on this would be greatly appreciated. I am just looking for a neat design pattern, or maybe I've missed something in the helper classes provided with Reactive Mongo or the respective helper classes.

Cheers,
Alex

Pascal Voitot Dev

unread,
Sep 25, 2012, 7:34:05 AM9/25/12
to play-fr...@googlegroups.com
Oh yes we've thought about that :)
It's one of the new features in Play2.1 : Json validators / combinators !
The new JSON API is already present in Play2 master but we are still working on it.

If you want to have a glance at this new API, have a look at my recent article:
http://mandubian.com/2012/09/08/unveiling-play-2-dot-1-json-api-part1-jspath-reads-combinators/
I should write Part2 soon (still working a bit on the API)

You can have a look at this very simple sample: https://github.com/zenexity/Play-ReactiveMongo/tree/master/samples/Play-ReactiveMongo-Sample

We are working on enhancing all of this for future Play2.1 so API is still draft.

Thanks
Pascal

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To view this discussion on the web visit https://groups.google.com/d/msg/play-framework/-/PJAnZp6HuHsJ.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.

Alex Jarvis

unread,
Sep 25, 2012, 7:43:35 AM9/25/12
to play-fr...@googlegroups.com
Sweet Jesus

Alex Jarvis

unread,
Sep 25, 2012, 9:17:14 AM9/25/12
to play-fr...@googlegroups.com
Thanks a lot for this. It's amazing work what you're doing with the API, it was exactly what I was searching for :)

I have a slight problem getting the validate.fold example working however as I have it wrapped by Async as required by Reactive Mongo.

e.g.
def create = Action(parse.json) { request =>
    Async { 
      val json = request.body
      json.validate[User].fold(
        valid = (res => 
          collection.insert[JsValue](res).map(lastError => 
            Ok("Mongo LastError:%s".format(lastError)))
        ),
        invalid = ( e => BadRequest(e.toString) )
      )
    }
  }

Provides the error on the last line (invalid = ..):
"type mismatch; found : play.api.mvc.SimpleResult[String] required: 
scala.concurrent.Future[play.api.mvc.Result]"

Is there a way to provide a Future of a BadRequest?

Also, for my userReads implicit val, I had to specify "(User.apply _)" e.g.
implicit val userReads = (
    (__ \ "email").read[String] and
    (__ \ "firstName").read[String] and
    (__ \ "lastName").read[String]
  )(User.apply _)

And if I change it to just have (User) at the end, as suggested it spurts out the following:
"- overloaded method value apply with alternatives: [B](f: B => (java.lang.String, java.lang.String, String))(implicit fu: 
play.api.libs.json.util.ContravariantFunctor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] <and> [B](f: (java.lang.String, java.lang.String, String) => B)(implicit fu: 
play.api.libs.json.util.Functor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] cannot be applied to (models.User.type)"

 (I have declared the implicit val inside the User object, as there was not a complete example and I figured it didn't belong in the Controller).

On Tuesday, 25 September 2012 12:34:38 UTC+1, Pascal wrote:

Pascal Voitot Dev

unread,
Sep 25, 2012, 9:34:57 AM9/25/12
to play-fr...@googlegroups.com
below

On Tue, Sep 25, 2012 at 3:17 PM, Alex Jarvis <alex.j...@gmail.com> wrote:
Thanks a lot for this. It's amazing work what you're doing with the API, it was exactly what I was searching for :)


Thanks ;)
 
I have a slight problem getting the validate.fold example working however as I have it wrapped by Async as required by Reactive Mongo.

e.g.
def create = Action(parse.json) { request =>
    Async { 
      val json = request.body
      json.validate[User].fold(
        valid = (res => 
          collection.insert[JsValue](res).map(lastError => 
            Ok("Mongo LastError:%s".format(lastError)))
        ),
        invalid = ( e => BadRequest(e.toString) )
      )
    }
  }


You should send a Pure Future[Result] such as :
        invalid = ( e => Future(BadRequest(e.toString)) )

This is Scala2.10 Future ;)
 
Provides the error on the last line (invalid = ..):
"type mismatch; found : play.api.mvc.SimpleResult[String] required: 
scala.concurrent.Future[play.api.mvc.Result]"

Is there a way to provide a Future of a BadRequest?

Also, for my userReads implicit val, I had to specify "(User.apply _)" e.g.
implicit val userReads = (
    (__ \ "email").read[String] and
    (__ \ "firstName").read[String] and
    (__ \ "lastName").read[String]
  )(User.apply _)

And if I change it to just have (User) at the end, as suggested it spurts out the following:
"- overloaded method value apply with alternatives: [B](f: B => (java.lang.String, java.lang.String, String))(implicit fu: 
play.api.libs.json.util.ContravariantFunctor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] <and> [B](f: (java.lang.String, java.lang.String, String) => B)(implicit fu: 
play.api.libs.json.util.Functor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] cannot be applied to (models.User.type)"

 (I have declared the implicit val inside the User object, as there was not a complete example and I figured it didn't belong in the Controller).


If you define outside the User object?

How is defined your User class? the syntax with "(User)" is only for case classes...

Pascal
 
To view this discussion on the web visit https://groups.google.com/d/msg/play-framework/-/TYeYpMrYC2MJ.

Alex Jarvis

unread,
Sep 25, 2012, 10:13:11 AM9/25/12
to play-fr...@googlegroups.com
I think I need to download Scala 2.10 for that Future to work.. is that correct? I built 2.1-SNAPSHOT using 2.9.2 I think, so maybe this will cause issues (or does it not use scala on my path?)

This is my User case class and object:

case class User(
  email: String,
  firstName: String,
  lastName: String
)
  
object User { 
  implicit val userReads = (
    (__ \ "email").read[String] and
    (__ \ "firstName").read[String] and
    (__ \ "lastName").read[String]
  )(User.apply _)
}

Pascal Voitot Dev

unread,
Sep 25, 2012, 10:51:41 AM9/25/12
to play-fr...@googlegroups.com
No you don't need scala 2.10...
Play 2.1-SNAPSHOT build is autonomous normally using "./build.sh"
And once built, you can use it directly without any local install of Scala...
Don't put Scala libs on your path, Play brings everything!

Pascal

To view this discussion on the web visit https://groups.google.com/d/msg/play-framework/-/Hv_cuxnkDi4J.
Reply all
Reply to author
Forward
0 new messages