JSON数组读取Scala API

时间:2016-12-09 10:50:55

标签: json scala playframework

我现在已经尝试了几件事来让这个API工作,但不知怎的,它只是赢了。 日志数组不会转换为List [Log]。 我正在使用reactivemongo和Scala的播放框架。

通过网络平台,以下json将到达API:

{ 
  name: 'name', 
  logs: [{timestamp: 1, activity: 'something'}]
}

现在我有两个模型,每个模型对应一个:

import play.api.libs.json.Json
  case class User(name: String, logs: List[Log])
  object User { implicit val userFormatter = Json.format[User]

import play.api.libs.json.Json
  case class Log(timestamp: String, activity: String)
  object Log { implicit val logFormatter = Json.format[Log]

在控制器中,我试图按如下方式读取JsObject:

@Singleton
class UserController @Inject()(val reactiveMongoApi: ReactiveMongoApi)(implicit exec: ExecutionContext) extends Controller with MongoController with ReactiveMongoComponents {


  val transformer: Reads[JsObject] =
      Reads.jsPickBranch[JsString](__ \ "name") and
      Reads.jsPickBranch[JsArray](__ \ "logs") reduce

  //get the user collection from db
  def persons: JSONCollection = db.collection[JSONCollection]("user")

  var clazz: Class[_ <: JsObject] = _

  //create a new user entry
  def create(name: String,
         logs: List[Log]) = Action.async {

val json = Json.obj(
  "name" -> name,
  "logs" -> logs)

persons.insert(json).map(lastError =>
  Ok("Mongo LastError: %s".format(lastError)))

}

路线如下: POST /user/add controllers.UserController.create(name: String, logs: List[models.Log])

它告诉我[error] /ARest-api/conf/routes:26: No QueryString binder found for type List[models.Log]. Try to implement an implicit QueryStringBindable for this type.

我试过了。我用以下代码替换了logFormatter代码行:

implicit def LogBinder(implicit stringBinder: QueryStringBindable[String]) =
new QueryStringBindable[Log] {

  override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Log]] = {
    Some({
      val timestamp = stringBinder.bind(key + ".timestamp", params)
      val activity = stringBinder.bind(key + ".activity", params)
      (timestamp, activity) match {
        case (Some(Right(timestamp)), Some(Right(activity))) => Right(Log(timestamp, activity))
        case _ => Left("Unable to bind Log")
      }
    })
  }

  override def unbind(key: String, log: Log): String =
    stringBinder.unbind(
      key + ".timestamp", log.timestamp) +
      "&" + stringBinder.unbind(key + ".activity", log.activity)
}

不幸的是,这不起作用。我现在已经好几天都在苦苦挣扎,无法让它发挥作用。 有人可以告诉我,是否有更好的方法可以做到这一点,或者帮助我修复阵列?

提前致谢!

1 个答案:

答案 0 :(得分:1)

不是最优雅的解决方案,但这至少是使其发挥作用的一种方法。

要在帖子请求中发送json,您可以执行以下操作:

  • 将路线更改为POST /user/add controllers.UserController.create,因为您将在帖子正文中获取用户信息。

  • 通过添加以下内容获取您的操作中的帖子消息:

    def create = Action.async(BodyParsers.parse.json) { request =>
    

    这将使用特定于JSON的BodyParser来解析请求,并将request.body作为JsValue提供。

现在我们可以使用案例类解析收到的json:

case class User(name: String, logs: List[Log])
case class Log(timestamp: String, activity: String)

Reads个对象:

implicit val logReads = Json.reads[Log]
implicit val userReads = (
  (JsPath \ "name").read[String] and
  (JsPath \ "logs").read[List[Log]]
)(User.apply _)

最后解析:

val user: Option[User] = Json.fromJson[User](request.body).asOpt


最终的代码看起来像这样:

@Singleton
class HomeController @Inject() extends Controller {
  implicit val logReads = Json.reads[Log]
  implicit val userReads = (
    (JsPath \ "name").read[String] and
    (JsPath \ "logs").read[List[Log]]
  )(User.apply _)

  def create = Action.async(BodyParsers.parse.json) { request =>

    val user: Option[User] = Json.fromJson[User](request.body).asOpt
    Future(Ok)
  }

}

case class User(name: String, logs: List[Log])
case class Log(timestamp: String, activity: String)

希望这有助于解决您的问题!