I’m trying to create a Post request path using the Akka HTTP Server and the Scala programming language.
After I successfully make and receive the post request data, I would like to access the value of the country
field inside the device
parameter.
What is the best way to access this parameter?
I am using SprayJSON as the marshaller.
The current code gives the error Found: Option[Option[String]] Required: String
.
Again, the request is processed successfully, I just can’t figure out how to access the value of the country
field inside the device
parameter.
The code is as below.
The case classes:
case class Geo(country: Option[String])
case class Device(id: String, geo: Option[Geo])
case class dataRequest(id: String, device: Option[Device])
The Post Request path:
Path(“submit”) {
Post {
Entity(as[dataRequest]) { data_request_params =>
data_request_params.device.map(dev => dev.geo.map(geo => {
val geoDevice = geo.country
}))
}
}
}
The request body:
{
“id”: “fghq1I123”,
“device”: {
“id”: “0004a672f”,
“geo”: {
“Country”: “AU”
}
}
}
It seems the problem you have, based on the question you asked in your previous post is not with akka neither akka-http. It's with the type system of scala.
First you need to understand how the Option
class work, and at the same time what is the difference between map
and flatMap
.
map
method has the signature Option[A].map[B](A => B): Option[B]
. Which means, it will transform the value contained inside the Option
(if the Option is of type Some
, otherwhise it will not do anything) from A
to B
. For example:// this produces a `Option[Int]`
val optionInt: Option[Int] = Option(1)
// this produces a `Option[String]` because the return type of the function I'm passing as parameter is a `String`
val optionString: Option[String] = optionInt.map(int => int.toString)
// this produces a `Option[Option[Boolean]` because the return type of the function is an `Option[Int]`
// remember that the map method expect a function A => B and returns B
// in the following example B is of type of Option
val optionOptionBoolean: Option[Option[Boolean]] =
optionInt
.map(int =>
optionString
.map(string => int == string)
)
flatMap
method has the signature Option[A].flatMap[B](A => Option[B]): Option[B]
. As you can see, the function expected is an Option[B]
and the return type is Option[B]
. This is the one that does the magic you need for this specific case// a class with an Option field
case class NestedOption(optionInt: Option[Int])
// a class with a field that is an Option of another class that contains an Option field
case class Root(nestedOption: Option[NestedOption])
val nestedOption: Option[NestedOption] = Some(NestedOption(1))
val root: Root = Root(nestedOption)
// to access the nested Option field and transform from Option[Int] to Option[String]
val optionString = root
.flatMap( nestedOption => // I have access to the field `nestedOption` of Root
nestedOption
.optionInt // this field is of type Option, where I can call `map`
.map( int => i.toString) // returning an Option[String] for the `flatMap`
)
// this can also be written much cleaner using for yield
val optionString = for {
nestedOption <- root
int <- nestedOption.nestedOption.optionInt
} yield int.toString
To fix the error in your code
Path(“submit”) {
Post {
Entity(as[dataRequest]) { data_request_params =>
// the code below will return an Option of the value type you return in the last line
// if some of the `Option`s are None, you will get a `None`
val anotherOption = for {
device <- data_request_params.device
geo <- device.geo
country <- geo.country
} yield // the logic you need to apply here
}
}
}
Here you have a good link to learn more about Option
At the same time, I suggest to first learn the basics of Scala.