I'm writing a Play 2.3.2 application, using Scala. I use ReactiveMongo to access a my mongoDB database.
I've a collection named recommendation.advices that store all the suggestion to the user.
A document has the following form:
{
"_id" : ObjectId("54475f434be669af141677b7"),
"id" : "b8e84cb9-4b3a-4c6c-b01d-af276747e4ad",
"user" : {
"id" : "",
"email" : "luigi@gmail.com"
},
"output" : [
{
"tag" : "Vegetable:Carrots - alberto@gmail.com",
"score" : 0
},
{
"tag" : "Paper Goods:Liners - Baking Cups",
"score" : 0
},
{
"tag" : "Vegetable:Carrots - Jumbo",
"score" : 0
},
{
"tag" : "Paper Goods:Lialberto- Baking Cups",
"score" : 0
}
],
"date" : 1413963587328,
"type" : "system",
"clicked" : true
}
Now I'm writing a method in my controller that returns all the data where "clicked": true
, and I want to return a Json in the following form:
{"idR": "b8e84cb9-4b3a-4c6c-b01d-af276747e4ad",
"user" : {
"id" : "",
"email" : "luigi@gmail.com"
},
"tags" : [
{
"tag" : "Vegetable:Carrots - alberto@gmail.com",
"score" : 0
},
{
"tag" : "Paper Goods:Liners - Baking Cups",
"score" : 0
},
{
"tag" : "Vegetable:Carrots - Jumbo",
"score" : 0
},
{
"tag" : "Paper Goods:Lialberto- Baking Cups",
"score" : 0
}
]
}
How can I make that in play??
I try to implement the method as the following:
def clickedAdvises = Action.async {
val query = Json.obj("clicked" -> true)
Advices.find(query).toList flatMap { advices =>
val results = for(el <- advices) yield Json.obj("idR" -> el.id, "user" -> el.user, "tags" -> el.output)
Future{Ok(Json.obj("results" -> results))}
}
}
The el.output is a List[(Tag, Double)],
and for the Tag I've defined a formatter.
But the compiler gives me the following errors:
[error] /Users/alberto/git/bdrim/modules/recommendation-system/app/recommendationsystem/controllers/StatisticsController.scala:111: type mismatch;
[error] found : List[(recommendationsystem.models.Tag, Double)]
[error] required: play.api.libs.json.Json.JsValueWrapper
[error] val results = for(el <- advices) yield Json.obj("idR" -> el.id, "user" -> el.user, "tags" -> el.output)
[error] ^
[error] /Users/alberto/git/bdrim/modules/recommendation-system/app/recommendationsystem/controllers/StatisticsController.scala:111: type mismatch;
[error] found : List[(recommendationsystem.models.Tag, Double)]
[error] required: play.api.libs.json.Json.JsValueWrapper
[error] val results = for(el <- advices) yield Json.obj("idR" -> el.id, "user" -> el.user, "tags" -> el.output)
[error] ^
[error] one error found
How can I fixed that??
The error is self explanatory:
You need JsValue
, instead you are giving a List[(recommendationsystem.models.Tag, Double)]
which apparently is the type of output
field in your Advice
class.
The fact that you have a formatter for your Tag
class does not change the situation, first because what you have is a List of tuples, not a single tag, second because formatter only works when you use toJson
and fromJson
macros.
So what you need is somehow convert this List
of tuples to a JsValue
.
JsArray
is corresponding to JSON list. If you look at the code of it
case class JsArray(value: Seq[JsValue] = List()) extends JsValue
you see that you can pass a Seq
of JsValue
s to it. So you need to somehow convert a tuple of (recommendationsystem.models.Tag, Double)
to a JsObject
(which is corresponding to a JSON object). You can do this simply with the following code:
JsObject(Seq(
("tag", tag),
("score", score)
))
or by using the Json.obj
which is a helper factory mehtod for constructing a JsObject
.
So the code will be:
val results = for {
el <- advices
} yield
Json.obj(
"idR" -> el.id,
"user" -> el.user,
"tags" -> Json.arr(el.output.map(it => Json.obj((it._1, it._2)).asInstanceOf[JsValueWrapper]): _*)
)
If you refer to Play Doc about JSON you will see that you can convert objects to/from json by using play.api.libs.json.Json.toJson
method. In order to do that you need an implicit writer for your classes which is pretty straightforward.
There are some macro helpers, for example Json.writes
macro helps you create a writer for your class. This machinery is recursive so if you have implicit writers for all classes needed, then you can simply call Json.toJson(advice)
and get the result.
So I would suggest to start writing an implicit writer for your advice class:
import play.api.libs.json._
implicit def adviceWriter = Json.writes[Advice]
then try
Json.toJson(advice)
Then compile the code, you will get compile errors because you need writer for all needed classes. Continue by providing a writer for them till you get no compile error!
This makes the code cleaner and more concise and focused, as the actual conversion to Json will not pollute the actual code. In addition, you can reuse the code without explicitly doing anything. You need just to bring implicit writers and readers to your context by importing them and using Json.toJson
and Json.fromJson
methods.
我认为您应该先将el.output转换为JsObject,然后再将其插入jsObject
Are you sure that el
object can be called by something like el.id
? It's not JavaScript, even it's JsValue, it should called by (el \\ "id")
.
I'm no pretty sure, but try using el(id)
to call it and insert as a value of your JsObject
like
Json.obj("idR" -> el(id))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.