LoginSignup
1
2

More than 5 years have passed since last update.

PlayFrameworkでJsonParse

Last updated at Posted at 2017-03-10

パースエラー内容を保持しない方法

import play.api.libs.json.{ Format, JsValue, Json }

case class HogeJson(
  id: String,
  data: String
)

object HogeJson {
  implicit val format: Format[HogeJson] = Json.format[HogeJson]
  def fromJson(json: JsValue): Option[HogeJson] = Json.fromJson(json)(format).asOpt
}

成功パターン

val str = """{ "id": "aa", "data": "bb" }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)
res12: Option[HogeJson] = Some(HogeJson(aa,bb))

失敗パターン(必須プロパティが空)

val str = """{ "id": "aa" }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)
res14: Option[HogeJson] = None

失敗するとNoneがかえるが、複雑なJsonだとどこでエラーになっているか調べるのが辛くなる。

パースエラー内容を保持する方法

import com.typesafe.scalalogging.LazyLogging
import scalaz.Scalaz.ToEitherOps
import scalaz.\/
import play.api.data.validation.ValidationError
import play.api.libs.json.{ Format, JsError, JsPath, JsSuccess, JsValue, Json }

case class HogeJson(
  id: String,
  data: String
)

object HogeJson extends LazyLogging {
  implicit val format: Format[HogeJson] = Json.format[HogeJson]
  def fromJson(json: JsValue) =
    Json.fromJson(json)(format).fold(
      valid = s => s.right,
      invalid = e => {
        e.foreach { case (path, errors) =>
          logger.error(s"""path:[${path.toString}] msg:[${errors.map(x => x.message).mkString(",")}] arg:[${errors.map(_.args.toString).mkString(",")}] """)
        }
        e.left
      }
    )
}

成功パターン

val str = """{ "id": "aa", "data": "bb" }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)
res12: Option[HogeJson] = Some(HogeJson(aa,bb))

失敗パターン1(必須プロパティが空)

val str = """{ "id": "aa" }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)
09:22:40.948 [run-main-0] ERROR $line79.$read$$iw$$iw$HogeJson$ - path:[/data] msg:[error.path.missing] arg:[WrappedArray()]

res50: scalaz.\/[Seq[(play.api.libs.json.JsPath, Seq[play.api.data.validation.ValidationError])],HogeJson] = -\/(List((/data,List(ValidationError(List(error.path.missing),WrappedArray())))))

失敗パターン2(型エラー)

val str = """{ "id": 1, "data": 2 }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)

09:23:40.447 [run-main-0] ERROR $line79.$read$$iw$$iw$HogeJson$ - path:[/data] msg:[error.expected.jsstring] arg:[WrappedArray()]
09:23:40.447 [run-main-0] ERROR $line79.$read$$iw$$iw$HogeJson$ - path:[/id] msg:[error.expected.jsstring] arg:[WrappedArray()]

res51: scalaz.\/[Seq[(play.api.libs.json.JsPath, Seq[play.api.data.validation.ValidationError])],HogeJson] = -\/(List((/data,List(ValidationError(List(error.expected.jsstring),WrappedArray()))), (/id,List(ValidationError(List(error.expected.jsstring),WrappedArray())))))

ネストもOK

import com.typesafe.scalalogging.LazyLogging
import scalaz.Scalaz.ToEitherOps
import scalaz.\/
import play.api.data.validation.ValidationError
import play.api.libs.json.{ Format, JsError, JsPath, JsSuccess, JsValue, Json }

case class HogeJson(
  id: String,
  data: HogeJson2
) {
  def toJson: JsValue = Json.toJson(this)(HogeJson.format)
}

object HogeJson extends LazyLogging {
  implicit val format: Format[HogeJson] = Json.format[HogeJson]
  def fromJson(json: JsValue) = Json.fromJson(json)(format).fold(
    valid = s => s.right,
    invalid = e => {
      e.foreach { case (path, errors) =>
        logger.error(s"""path:[${path.toString}] msg:[${errors.map(x => x.message).mkString(",")}] arg:[${errors.map(_.args.toString).mkString(",")}] """)
      }
      e.left
    }
  )
}

case class HogeJson2(value: String) {
  def toJson: JsValue = Json.toJson(this)(HogeJson2.format)
}
object HogeJson2 extends LazyLogging {
  implicit val format: Format[HogeJson2] = Json.format[HogeJson2]
}

成功パターン

val str = """{ "id": "1", "data": { "value": "a" } }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)

res57: scalaz.\/[Seq[(play.api.libs.json.JsPath, Seq[play.api.data.validation.ValidationError])],HogeJson] = \/-(HogeJson(1,HogeJson2(a)))

失敗パターン2(HogeJson2の型エラー)

val str = """{ "id": "1", "data": { "value": 1 } }"""
val jsValue = Json.parse(str)

scala> HogeJson.fromJson(jsValue)

10:06:46.333 [run-main-0] ERROR $.$read$$iw$$iw$$iw$$iw$HogeJson$ - path:[/data/value] msg:[error.expected.jsstring] arg:[WrappedArray()]
res59: scalaz.\/[Seq[(play.api.libs.json.JsPath, Seq[play.api.data.validation.ValidationError])],HogeJson] = -\/(List((/data/value,List(ValidationError(List(error.expected.jsstring),WrappedArray())))))

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2