1

play フレームワークに基づく json rest api アプリケーションがあり、着信要求を解析するときに検証エラーに関する情報を受け取りたいと考えています。json 配列から json 値への変換を除いて、すべて正常に動作しています。私が達成したいJson構造:

{
  "errors": {
    "name": ["invalid", "tooshort"],
    "email": ["invalid"]
  }
}

サンプルを実装しようとすると、完全に機能しました。

def error = Action {
  BadRequest(obj(
    "errors" -> obj(
      "name" -> arr("invalid", "tooshort"),
      "email" -> arr("invalid")
    )
  ))
}

このように変化部分を抽出しようとすると:

def error = Action {
  val e = Seq("name" -> arr("invalid", "tooshort"), "email" -> arr("invalid"))
  // in real app it will be Seq[(JsPath, Seq[ValidationError])] 
  BadRequest(obj(
    "errors" -> obj(e: _*)
  ))
}

コンパイラ エラーが発生しました。

型の不一致; 見つかった: Seq[(String, play.api.libs.json.JsArray)] 必須: Seq[(String, play.api.libs.json.Json.JsValueWrapper)]

JsArray から JsValueWrapper への暗黙の変換が欠けているのではないでしょうか? しかし、なぜサンプルは同じファイルで同じインポートで正常に動作するのでしょうか?

Play 2.1.1、Scala 2.10.0

更新: 最終コードの Julien Lafont のおかげで問題が解決しました:

implicit val errorsWrites = new Writes[Seq[(JsPath, Seq[ValidationError])]] {
  /**
   * Maps validation result of Ember.js json request to json validation object, which Ember can understand and parse as DS.Model 'errors' field.
  *
  * @param errors errors collected after json validation
  * @return json in following format:
  *
  * {
  *   "errors": {
  *     "error1": ["message1", "message2", ...],
  *     "error2": ["message3", "message4", ...],
  *     ...
  *   }
  * }
  */
 def writes(errors: Seq[(JsPath, Seq[ValidationError])]) = {
   val mappedErrors = errors.map {
     e =>
       val fieldName = e._1.toString().split("/").last // take only last part of the path, which contains real field name
       val messages = e._2.map(_.message)
       fieldName -> messages
   }

   obj("errors" -> toJson(mappedErrors.toMap)) // Ember requires root "errors" object
  }
}

使用法:

def create = Action(parse.json) { // method in play controller
  request =>
    fromJson(request.body) match {
      case JsSuccess(pet, path) => Ok(obj("pet" -> Pets.create(pet)))
      case JsError(errors) => UnprocessableEntity(toJson(errors)) // Ember.js expects 422 error code in case of errors
    }
}
4

2 に答える 2

4

でエラーを定義し、Map[String, Seq[String]]それを Json に変換するだけですJson.toJson( と の組み込みライターがMap[String,Y]ありますSeq[X]) 。

scala> val e = Map("name" -> Seq("invalid", "tooshort"), "email" -> Seq("invalid"))
e: scala.collection.immutable.Map[String,Seq[String]] = Map(name -> List(invalid, tooshort), email -> List(invalid))

scala> Json.toJson(e)
res0: play.api.libs.json.JsValue = {"name":["invalid","tooshort"],"email":["invalid"]}

scala> Json.obj("errors" -> Json.toJson(e))
res1: play.api.libs.json.JsObject = {"errors":{"name":["invalid","tooshort"],"email":["invalid"]}}
于 2013-07-23T19:45:10.610 に答える
2

ロングハンド バージョンが機能する理由は、scala の自動型推論が暗黙の変換をトリガーするためtoJsFieldJsValueWrapperです。

例えば

scala> import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json._

scala> import play.api.libs.functional.syntax._
import play.api.libs.functional.syntax._

scala> import Json._
import Json._

scala> arr("invalid", "tooshort")
res0: play.api.libs.json.JsArray = ["invalid","tooshort"]

scala> obj("name" -> arr("invalid", "tooshort"))
res1: play.api.libs.json.JsObject = {"name":["invalid","tooshort"]}

scala> obj _
res3: Seq[(String, play.api.libs.json.Json.JsValueWrapper)] => play.api.libs.json.JsObject = <function1>

arrは を返しますがJsArray、 がobj必要であることに注意してくださいJsValueWrapper。Scala はの引数を構築JsArrayするときに を に変換できます。ただし、aを Seq[(String, JsValueWrapper)] に変換することはできません。JsValueWrapperobjSeq[(String, JsArray)]

事前にシーケンスの期待される型を指定すると、Scala コンパイラの型推論は、シーケンスを作成するときに変換を実行します。

scala> Seq[(String, JsValueWrapper)]("name" -> arr("invalid", "tooshort"), "email" -> arr("invalid"))
res4: Seq[(String, play.api.libs.json.Json.JsValueWrapper)] = List((name,JsValueWrapperImpl(["invalid","tooshort"])), (email,JsValueWrapperImpl(["invalid"])))

ただし、シーケンスが作成されると、スコープ内にシーケンスを変換できる暗黙的な変換がない限り、変換できません。

最終的なコード スニペットは次のようになります。

def error = Action {
  val e = Seq[(String, JsValueWrapper)]("name" -> arr("invalid", "tooshort"), "email" -> arr("invalid"))
  // in real app it will be Seq[(JsPath, Seq[ValidationError])] 
  BadRequest(obj(
    "errors" -> obj(e: _*)
  ))
}
于 2013-07-24T11:58:20.723 に答える