我现在已经尝试了几件事来让这个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)
}
不幸的是,这不起作用。我现在已经好几天都在苦苦挣扎,无法让它发挥作用。 有人可以告诉我,是否有更好的方法可以做到这一点,或者帮助我修复阵列?
提前致谢!
答案 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)
希望这有助于解决您的问题!