使用play框架在Json值上转换List [(T,Double)]

时间:2014-10-22 08:57:02

标签: json scala playframework

我正在使用Scala编写Play 2.3.2应用程序。 我使用ReactiveMongo来访问我的mongoDB数据库。

我有一个名为recommendation.advices的集合,它将所有建议存储到用户。

文件格式如下:

{
    "_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
}

现在我正在我的控制器中编写一个方法,它返回"clicked": true的所有数据,我想以下列形式返回一个Json:

{"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
        }
    ]
}

我怎样才能发挥其作用?

我尝试按以下方式实现该方法:

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))}

       }
     } 

el.output是List[(Tag, Double)],,对于Tag我已经定义了格式化程序。

但是编译器给了我以下错误:

[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

我该如何解决?

3 个答案:

答案 0 :(得分:1)

错误是不言自明的:

您需要JsValue,而不是List[(recommendationsystem.models.Tag, Double)]outputAdvice字段显示Tag字段。

您拥有toJson类的格式化程序这一事实并不会改变这种情况,首先是因为您拥有的是元组列表,而不是单个标记,其次是因为格式化程序仅在您使用{{{ 1}}和fromJson宏。

所以你需要的是以某种方式将这个List元组转换为JsValue

JsArray对应于JSON列表。如果你看一下它的代码

case class JsArray(value: Seq[JsValue] = List()) extends JsValue 

您发现可以将Seq JsValue传递给它。因此,您需要以某种方式将(recommendationsystem.models.Tag, Double)的元组转换为JsObject(对应于JSON对象)。您只需使用以下代码即可完成此操作:

JsObject(Seq(
  ("tag", tag),
  ("score", score)
))

或使用Json.obj作为帮助工厂mehtod来构建JsObject

所以代码将是:

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]): _*)
                        )

更简单!

如果您引用Play Doc about JSON,您将看到可以使用play.api.libs.json.Json.toJson方法将对象转换为json或从json转换对象。 为了做到这一点,你需要为你的类隐式编写器,这是非常简单的。

有一些宏助手,例如Json.writes宏可以帮助您为类创建一个编写器。这个机制是递归的,所以如果你有所需的所有类的隐式编写器,那么你只需调用Json.toJson(advice)并获得结果。

所以我建议你开始为你的建议课写一个隐含的作家:

import play.api.libs.json._

implicit def adviceWriter = Json.writes[Advice]

然后尝试

Json.toJson(advice)

然后编译代码,你将得到编译错误,因为你需要编写所有需要的类。继续为他们提供一个编写器,直到你没有编译错误!

这使代码更清晰,更简洁,更集中,因为实际转换为Json不会污染实际代码。此外,您可以重用代码而无需显式执行任何操作。您只需要通过导入并使用Json.toJsonJson.fromJson方法将隐式编写者和读者带入您的上下文。

答案 1 :(得分:0)

我认为你应该在将el.output插入jsObject

之前将其转换为JsObject

答案 2 :(得分:0)

您确定可以通过el之类的内容调用el.id对象吗?它不是JavaScript,即使它是JsValue,也应该由(el \ "id")调用。

我不太确定,但尝试使用el(id)来调用它并插入JsObject的值

Json.obj("idR" -> el(id))