Recently, I used spray to do something. At the beginning, I started to get started with the doc. the most flexible way is to count its routing-DSL. Of course, due to Scala's powerful features, many different from Java, the source code looks quite laborious, especially for beginners who haven't used Scala for a long time.
The problem today is how to convert a parameter to the desired type, for example? Age = 25 & birth = 1988-01-23, can receive the int and date parameters. Soon I found the method in the document, as shown in the following code:
case class Color(red: Int, green: Int, blue: Int)val route = path("color") { parameters(‘red.as[Int], ‘green.as[Int], ‘blue.as[Int]) { (red, green, blue) => val color = Color(red, green, blue) doSomethingWith(color) // route working with the Color instance } }
However, when I write it myself, I find that writing the date will cause a compilation error,
val route: Route = path("stat" / Rest) { path => parameters(‘age.as[Int], ‘birth.as[java.util.Date]) { (age, birth) => get { complete { path + age.toString + birth } } } }
[info] Compiling 1 Scala source to D:\study\spray-template\target\scala-2.11\classes...[error] D:\study\spray-template\src\main\scala\com\example\MyService.scala:60: too many arguments for method parameters: (pdm: spray.routing.directives.ParamDefMagnet)pdm.Out[error] parameters(‘age.as[Int], ‘birth.as[java.util.Date]) { (age, birth) =>[error] ^
I don't know what to say about the error. The classic method did not find a satisfactory answer when I googled the error message. Then I started to think about it myself. Spray or Scala has no reason to be so powerful that you can write a Type A and as [a] to convert string, there must be something magic happening behind the scenes.
So Ctrl + B (idea shortcut key, I don't want to switch back to eclipse after idea is used, this is not an advertisement). After clicking it, I found it was a case class namereceptacle, it is not the directly returned type A as I imagined. Continue to view parameters. After clicking it, it will be:
def parameters(pdm: ParamDefMagnet): pdm.Out = pdm()
Then I am stupid, and I am just a bunch of implicit stuff. I don't know what I'm talking about! Then I went to the doc and found the magnet pattern. Then I seemed to have seen hope. Click it. Let's take a long e article and feel timid! However, I still finished reading it later. Simply put, it is Scala's design pattern to solve some shortcomings caused by the Type erasure of traditional method overloading. Then, because 'Age. As [int] returns the namereceptacle [int] type, the "overload" method is found.
implicit def forNR[T](implicit fsod: FSOD[T]) = extractParameter[NameReceptacle[T], T] { nr ? filter(nr.name, fsod) }Can you see that it requires an implicit fsod? This is the thing.
import spray.httpx.unmarshalling.{ FromStringOptionDeserializer ? FSOD, _ }
type FromStringOptionDeserializer[T] = Deserializer[Option[String], T]
It is clear that spray has no reason to convert string to a for any type. It turns out that a deserializer is needed to do this. Because it is implicit, you do not need to write it out. 'Age. As [int] should be the implicit function that defines deserializer [Option [String], int] by default, which converts string to int. If no error is reported for date, deserializer [Option [String], date] is not available. Let's take a look at the definition.
implicit def string2Date = new Deserializer[Option[String], Date] { override def apply(v1: Option[String]): Deserialized[Date] = v1 match { case None => Left(ContentExpected) case Some(str) => { val sdf = new SimpleDateFormat("yyyy-MM-dd") try { val date = sdf.parse(str) Right(date) } catch { case _: Throwable => Left(MalformedContent("format yyyy-MM-dd")) } } } }
Compile again, no problem, run OK!
Later, we found that there is a method in object deserializer that can "Upgrade" the common function F: A => B to deserializer.
implicit def fromFunction2Converter[A, B](implicit f: A ? B) = new Deserializer[A, B] { def apply(a: A) = { try Right(f(a)) catch { case NonFatal(ex) ? Left(MalformedContent(ex.toString, ex)) } } }
Therefore, it can be simplified
implicit def str2Date(str: String) = { val sdf = new SimpleDateFormat("yyyy-MM-dd") sdf.parse(str) }
It looks refreshing!
Finally, let's talk about a small trick. At first I didn't understand the type of 'Age. As [int]. One way is to press Ctrl + B to see it. Another way is to define
val x = ‘age.as[Int]
Then move the mouse to X, ALT + enter, and select "add type annotation to value definition ".
val x: NameReceptacle[Int] = ‘age.as[Int]
This is convenient in some cases.
Scala is indeed very powerful and flexible, with a high learning curve and continuous efforts ........
Spray parameter conversion