玩json,递归读取,npe

时间:2016-05-18 09:31:48

标签: scala nullpointerexception play-json

我有两个案例类:

首先:

object Person {
  implicit val jsonFormat = Json.format[Person]
}

case class Person(name: String, coWorkers: List[CoWorker])

人可能有0或某些CoWorkers

第二

object CoWorker {
  implicit val jsonFormat: Format[CoWorker] = Json.format[CoWorker]
}

case class CoWorker(position: String, person: Person)

我做了测试:

import org.specs2.mutable.Specification
import play.api.libs.json.{JsError, JsArray, Json}

class NestedSpec extends Specification {

  "Nested" should {

    "jsonReads" in {

      val personJson = Json.obj(
        "name" -> "alex",
        "coWorkers" -> Json.arr(
          Json.obj(
            "person" -> Json.obj(
              "name" -> "jack",
              "coWorkers" -> List.empty[String]
            ),
            "position": "developer" 
          )
        )
      )

      val res = personJson.validate[Person].asEither

      res.left.map(err => println(Json.prettyPrint(JsError.toJson(err))))

      res.isRight must beTrue

    }

  }

}

但该测试失败并出现错误:

> testOnly  nested.NestedSpec
[info] NestedSpec
[info] 
[info] Nested should
[error]   ! jsonReads
[error]    java.lang.NullPointerException: null (JsConstraints.scala:32)
[error] play.api.libs.json.PathReads$$anonfun$at$1$$anonfun$apply$2.apply(JsConstraints.scala:32)
[error] play.api.libs.json.PathReads$$anonfun$at$1$$anonfun$apply$2.apply(JsConstraints.scala:32)
[error] play.api.libs.json.JsResult$class.flatMap(JsResult.scala:99)
[error] play.api.libs.json.JsSuccess.flatMap(JsResult.scala:9)
[error] play.api.libs.json.PathReads$$anonfun$at$1.apply(JsConstraints.scala:32)
[error] play.api.libs.json.PathReads$$anonfun$at$1.apply(JsConstraints.scala:32)
[error] play.api.libs.json.Reads$$anon$8.reads(Reads.scala:126)
[error] play.api.libs.json.OFormat$$anon$2.reads(Format.scala:46)
[error] play.api.libs.json.Reads$$anon$3$$anon$4.reads(Reads.scala:104)
[error] play.api.libs.json.OFormat$$anon$2.reads(Format.scala:46)
[error] play.api.libs.json.OFormat$$anon$5$$anonfun$inmap$1.apply(Format.scala:31)
[error] play.api.libs.json.OFormat$$anon$5$$anonfun$inmap$1.apply(Format.scala:31)
[error] play.api.libs.json.OFormat$$anon$1.reads(Format.scala:39)
[error] play.api.libs.json.Json$.fromJson(Json.scala:125)
[error] play.api.libs.json.LowPriorityDefaultReads$$anon$2$$anonfun$reads$1.apply(Reads.scala:168)
[error] play.api.libs.json.LowPriorityDefaultReads$$anon$2$$anonfun$reads$1.apply(Reads.scala:167)
[error] play.api.libs.json.LowPriorityDefaultReads$$anon$2.reads(Reads.scala:167)
[error] play.api.libs.json.DefaultFormat$$anon$4.reads(Format.scala:82)
[error] play.api.libs.json.PathReads$$anonfun$at$1$$anonfun$apply$2.apply(JsConstraints.scala:32)
[error] play.api.libs.json.PathReads$$anonfun$at$1$$anonfun$apply$2.apply(JsConstraints.scala:32)
[error] play.api.libs.json.JsResult$class.flatMap(JsResult.scala:99)
[error] play.api.libs.json.JsSuccess.flatMap(JsResult.scala:9)
[error] play.api.libs.json.PathReads$$anonfun$at$1.apply(JsConstraints.scala:32)
[error] play.api.libs.json.PathReads$$anonfun$at$1.apply(JsConstraints.scala:32)
[error] play.api.libs.json.Reads$$anon$8.reads(Reads.scala:126)
[error] play.api.libs.json.OFormat$$anon$2.reads(Format.scala:46)
[error] play.api.libs.json.Reads$$anon$3$$anon$4.reads(Reads.scala:104)
[error] play.api.libs.json.OFormat$$anon$2.reads(Format.scala:46)
[error] play.api.libs.json.OFormat$$anon$5$$anonfun$inmap$1.apply(Format.scala:31)
[error] play.api.libs.json.OFormat$$anon$5$$anonfun$inmap$1.apply(Format.scala:31)
[error] play.api.libs.json.OFormat$$anon$1.reads(Format.scala:39)
[error] play.api.libs.json.JsValue$class.validate(JsValue.scala:18)
[error] play.api.libs.json.JsObject.validate(JsValue.scala:76)
[error] nested.NestedSpec$$anonfun$1$$anonfun$apply$2.apply(NestedSpec.scala:24)
[error] nested.NestedSpec$$anonfun$1$$anonfun$apply$2.apply(NestedSpec.scala:10)
[info] 
[info] 
[info] 
[info] Total for specification NestedSpec
[info] Finished in 269 ms
[info] 1 example, 0 failure, 1 error
[info] 
[error] Error: Total 1, Failed 0, Errors 1, Passed 0
[error] Error during tests:
[error]         nested.NestedSpec
[error] (test:testOnly) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 1 s, completed May 18, 2016 12:25:16 PM

我认为这是一种课程依赖错误。 CoWorker使用Person的jsonReads,Person使用CoWorker jsonReads。有可能解决这个问题并让测试运行吗?

1 个答案:

答案 0 :(得分:1)

我需要使用lazyReads,可以在docs中找到:

  

递归类型我们的示例模型没有的一种特殊情况   演示如何处理递归类型的读取和写入。   JsPath提供了call-by-name的lazyRead和lazyWrite方法   处理这个的参数:

case class User(name: String, friends: Seq[User])

implicit lazy val userReads: Reads[User] = (
  (__ \ "name").read[String] and
  (__ \ "friends").lazyRead(Reads.seq[User](userReads))
)(User)

implicit lazy val userWrites: Writes[User] = (
  (__ \ "name").write[String] and
  (__ \ "friends").lazyWrite(Writes.seq[User](userWrites))
)(unlift(User.unapply))

https://www.playframework.com/documentation/2.5.x/ScalaJsonCombinators#recursive-types