PlayFrameworkでJsonParse



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

```
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())))))

```