2

我有一个虚拟类Foo,它有三个成员:

import play.api.libs.json.Json

case class Foo(id: String, fooType: FooType, nextId: Option[String])

object Foo {
  implicit val fooReads = Json.reads[Foo]
  implicit val fooFormat = Json.format[Foo]
}

其中FooType定义为

import play.api.libs.json.Json

case class FooType(a: String, b: String)

object FooType {
  implicit val fooTypeReads = Json.reads[FooType]
  implicit val fooTypeFormat = Json.format[FooType]
}

Foo我在序列化对象时发现了一些有趣的行为。如果我将 a 序列化为FooJSON,解析 JSONified Foo,我发现所有成员都被正确解析:

val id = "id"
val fooType = FooType("a", "b")
val nextId = None

val foo = Foo(id, fooType, nextId)
val jsonFoo = Json.toJson(foo)
val parsedFoo = Json.parse(jsonFoo.toString).as[Foo]

assert(parsedFoo == foo)
assert(parsedFoo.id == id)
assert(parsedFoo.fooType == fooType)
assert(parsedFoo.nextId.isEmpty)

这很好,因为这是我所期望的。

但是,在我的下一个测试中,我发现该nextId字段根本不可序列化:

val id = "id"
val fooType = FooType("a", "b")
val nextId = None

val foo = Foo(id, fooType, nextId)
val jsonFoo = Json.toJson(foo)

assert((jsonFoo \ "id").as[String] == id)
assert((jsonFoo \ "fooType").as[FooType] == fooType)
assert((jsonFoo \ "nextId").as[Option[String]].isEmpty)

这失败并出现以下错误:

Error:(38, 35) No Json deserializer found for type Option[String]. Try to implement an implicit Reads or Format for this type.
    assert((jsonFoo \ "nextId").as[Option[String]].isEmpty)

Error:(38, 35) not enough arguments for method as: (implicit fjs: play.api.libs.json.Reads[Option[String]])Option[String].
Unspecified value parameter fjs.
    assert((jsonFoo \ "nextId").as[Option[String]].isEmpty)

同样,我发现当我打印由 转储的 JSON 对象时,JSON 对象中缺少Json.toJson(foo)该字段:nextId

println(Json.prettyPrint(jsonFoo))

{
  "id" : "id",
  "fooType" : {
    "a" : "a",
    "b" : "b"
  }
}

但是,我可以使用;解析该nextId字段。toOptionIE,

assert((jsonFoo \ "nextId").toOption.isEmpty)

如果我的对象之一不是本机可反序列化的,如何从 JSON 中正确解析我的对象?

4

1 回答 1

2

nextId字段是可序列化的,否则,您根本无法将 a 写入FooJSON。

jsonFoo: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"}}

您遇到的问题是没有Readsfor Option[A]。选项由 Play JSON 专门处理。当使用 JSON 组合器时,我们使用readNullable[A]andwriteNullable[A]代替read[Option[A]]and write[Option[A]]。同样,当使用方法从 a 中提取单个字段时JsValue,调用as将不起作用,因为它需要Reads[A]为您提供的类型隐式(在这种情况下 a Reads[Option[String]],它不存在)。

相反,您需要使用asOpt,它将正确处理Option下面的内容:

scala> (jsonFoo \ "nextId").asOpt[String]
res1: Option[String] = None

nextId不会出现在打印的 JSON 中,因为您要序列化的值为None. 这是预期会发生的事情。由于该值是可选的,因此它会从 JSON 中省略(它只是undefined在 JavaScript 中)。如果它有一个值,它将出现:

scala> Json.toJson(Foo("id", FooType("a", "b"), Option("abcdef")))
res3: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"},"nextId":"abcdef"}

如果某些东西不能用 Play JSON 序列化,它根本就不会编译。

于 2017-02-26T14:29:28.423 回答