passing anonymous case classes as arguments to requestBody.extract

646 views
Skip to first unread message

Jeff Dyke

unread,
Jan 15, 2016, 9:20:06 PM1/15/16
to scala-user
That title could be simplified, but its what i'm trying to do.

example and i'm not going to post all the bad code i've tried....but enough to give you a taste of my thought process.

case class User(user:Int)
case class Foo(bar:String)
def parseRequest[T,V](classModel : T): V = {
  val parsed = Try(requestBody.extract[classModel])  //request is an implicit an in scope. 
  parsed match {
    case Failure(e) => doExceptionHanldingThatExits(e)
    case Success(_) => 
  }
  parsed.get
}

val userResult = parseRequest(User) //ohnoes!!!

The issue is that classModel is not known (unknown symbol), Intellij wants me to write it as classModel.type, but then i get an error message like:

[error]  found   : classModel.type (with underlying type T)
[error]  required: AnyRef
[error] Note that T is unbounded, which means AnyRef is not a known parent.
[error] Such types can participate in value classes, but instances
[error] cannot appear in singleton types or in reference comparisons.

I'm not sure that just having it be AnyRef will fully solve my issue as i need to be able to extract data from the result, but getting this to compile to vastly increase my understanding (yes i'm a noob to scala, but digging it!)
Any pointers or assistance is greatly appreciated, this is an area of scala, i'd like to have a much firmer understanding of as it will open many doors and increase DRY :)

Happy Friday (if you're still in a US Timezone)

Thanks,
Jeff

John

unread,
Jan 15, 2016, 10:43:56 PM1/15/16
to scala-user
Doesn't the type parameter to extract need to be a type, not an instance? (So extract[T] or extract[classOf[variable]])

What you have written is the equivalent to the following in java:

Object obj = ...
List<obj> blah

Sorry if im misunderstanding,

-John

Jeff Dyke

unread,
Jan 16, 2016, 3:14:53 PM1/16/16
to scala-user
Thanks John.   Food for thought, which makes me realize i didn't ask a clear question, b/c its not that extract is throwing an error, its that the variable/type is not recognized when used in any circumstance inside the method, other than plain println(model).  So this would also throw a compile error of "not found: type model".
case class User(name:String="Joe")

def parseRequest[T](model:T): Unit  = {
  println(classOf[model])
}

Perhaps i should start a new thread, ultimately i'm trying to use scala's type inference with locally defined entities, in this instance a case class, but even if i run parseRequest("foo"), i get the same compile error. (not found: type model)

Jeff

Jeff Dyke

unread,
Jan 16, 2016, 4:24:15 PM1/16/16
to scala-user
while the error may make this obvious, this is only an issue when you supply it to something that requires a generic type, so classOf, isInstanceOf, asInstanceOf and of course parsedBody.extract[]

Clint Gilbert

unread,
Jan 16, 2016, 6:13:51 PM1/16/16
to scala...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

One more thing: classOf[] needs a type. This won't work:

val s = "foo"

classOf[s]

This will:

classOf[String]

It's the equivalent of String.class in Java. classOf[T] gives you an
instance of type Class[T], an object representing an object's class
(in the JVM sense).

If you need to get the Class object for a particular instance, you can d
o

s.getClass

just like Java.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (GNU/Linux)

iEYEARECAAYFAlaazpoACgkQ0GFaTS4nYxvUwgCfYe9ztWiHoCG2WGLZ08SS7zuE
ss4AoMXrD3jvQAp6mmb+8KMuHdJEcffJ
=g//x
-----END PGP SIGNATURE-----

John

unread,
Jan 17, 2016, 2:17:08 AM1/17/16
to scala-user
I brain-farted a bit by saying classOf in my previous response -- like extract, classOf requires a type parameter, not a value (as  Clint pointed out).

I think you want extract[T].

The reason you get the compile error of "not found type model" is because there is no such type, any more than there is a type called classModel in the first example.

It's a namespace problem.  Variable name namespace and type name namespaces are separate (in practice, not sure about under the hood).    classModel and model are variable names, not type names, and therefore exist in the variable name namespace.  When you do extract[classModel] or classOf[model], you're telling Intellij and/or the compiler to search type namespace for a  type called "model" or "classModel."  No such type exists, hence the error.

-John

Jeff Dyke

unread,
Jan 18, 2016, 8:33:34 AM1/18/16
to scala-user
i appreciate the comments and understood the differences at first, but better now, so this has been helpful, to at least have them confirmed.  the main goal was to try to avoid writing
(some version of)
try {
  val parsed = parsedBody.extract[caseModel]
} catch {
  case ....
}

in each action of each controller/servlet where JSON needs to be extracted into a useable entity.  Perhaps i'm heading down an incorrect path. I like the typed members of case classes adding some benefits of user input checking, albeit not perfect, but still helpful.

Thanks.  For now i'll refactor in a different way to hopefully avoid repeating myself too much.

Jeff

John

unread,
Jan 18, 2016, 11:31:54 AM1/18/16
to scala-user
Ah, I'm probably missing the bigger picture.  Good luck with your refactor.  Just in case it helps, here's some sample code that defines a helper method that can be re-used in each controller action to avoid the try-to-deserialize boilerplate:

case class User(user:Int)
case class Foo(bar:String)

trait Deserializer[T] {
 
def parse(raw: String) : T
}
object Deserializer
{

 
implicit object ParseToUser extends Deserializer[User] {
   
override def parse(raw: String) : User = User(1)
 
}

 
implicit object ParseToFoo extends Deserializer[Foo] {
   
override def parse(raw: String) : Foo = Foo(raw)
 
}

 
def fakeExtract[T](raw: String)(implicit parser : Deserializer[T]) : scala.util.Try[T] = {
    scala
.util.Success(parser.parse(raw))
 
}
}

case class HttpRequest(body: String)
case class HttpResponse(status: Int)

object ControllerHelper
{
 
//Notice the implicit parameter (which will be passed through to extract)
  def parseRequest[T](request: HttpRequest)(f: T => HttpResponse)(implicit parser : Deserializer[T]) : HttpResponse = {  
   
Deserializer.fakeExtract[T](request.body) match {
     
case scala.util.Failure(_) =>
       
HttpResponse(400) //we couldn't parse the input... assume input is bad
      case scala.util.Success(parsed) =>
        f
(parsed)
   
}
 
}
}
object Controller1
{  
 
 
def action1(request: HttpRequest) : HttpResponse = ControllerHelper.parseRequest[User](request){
    user
=>
     
assert(user.isInstanceOf[User])      
     
HttpResponse(200)
 
}

 
def action2(request: HttpRequest) : HttpResponse = ControllerHelper.parseRequest[Foo](request){
    foo
=>
     
assert(foo.isInstanceOf[Foo])
     
HttpResponse(200)
 
}
 
}
object Main {
 
def main(args: Array[String]) : Unit = {
   
//simulate http request
    println(Controller1.action1(HttpRequest("user request")))
   
println(Controller1.action1(HttpRequest("foo request")))
 
}
}

Jeff Dyke

unread,
Jan 19, 2016, 3:22:15 PM1/19/16
to John, scala-user
Hey John.  I don't think you're missing the bigger picture as much as i should have stuck with my original question in a stricter sense.  This was definitely a great help, i ended up doing it a bit different, as i need to process the data after its parsed/returned by the implicit objects in the controller and i think i can improve on that.  But again a great help!

Thanks!

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Message has been deleted

Jeff Dyke

unread,
Jan 25, 2016, 10:20:57 AM1/25/16
to scala-user, johnw...@gmail.com
A post to hopefully help others.  Ultimately i ended up solving this with Manifests which is much more concise than my previous version.  I can't share all the code that calls it or the tests b/c of the project i'm working on but this is the file contents: 

JsonExtractor.scala
import org.json4s._
import org.json4s.ext.JodaTimeSerializers
import org.json4s.native.JsonParser


/**
* Created by jeff on 1/24/16.
*/
object JsonExtractor {
implicit val formats: Formats = DefaultFormats ++ JodaTimeSerializers.all

def extract[T : Manifest](raw:String): T = JsonParser.parse(raw).extract[T]

}

Then from other files i'm simply calling
val userData = JsonExtractor.extract[UserCreateModel](request.body)

This will throw a MappingException, if there is something in UserCreateModel that is not present in request.body and required, but that is simple to handle.   Just to state the obvious UserCreateModel is a local type(case class) i have, defined in the controller/servlet only.

Next step will be, when there is a bit of time, to move to TypeTag as opposed to Manifest, but for now, this suites my needs perfectly and allows the code inside the servlets/controllers to be obvious and increased DRY.  TypeTag was giving me a few errors with $evidence handling, so just need to learn a bit more about them.

HTH
Jeff
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages