为什么 Option[String] 类型的成员没有被序列化?
Why isn't a member of type Option[String] being serialized?
我有一个虚拟 class 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
对象时发现一些有趣的行为。如果我将 Foo
序列化为 JSON,解析 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.toJson(foo)
转储的 JSON 对象时,JSON 对象缺少 nextId
字段:
println(Json.prettyPrint(jsonFoo))
{
"id" : "id",
"fooType" : {
"a" : "a",
"b" : "b"
}
}
但是,我可以用 toOption
解析 nextId
字段;即,
assert((jsonFoo \ "nextId").toOption.isEmpty)
如果我的对象的其中一个成员本身不可反序列化,如何才能从 JSON 中正确解析我的对象?
nextId
字段是可序列化的,否则,您根本无法将 Foo
写入 JSON。
jsonFoo: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"}}
您遇到的问题是 Option[A]
没有 Reads
。选项由 Play JSON 专门处理。当使用 JSON 组合器时,我们使用 readNullable[A]
和 writeNullable[A]
而不是 read[Option[A]]
和 write[Option[A]]
。同样,当使用方法从 JsValue
中提取单个字段时,调用 as
将不起作用,因为它需要隐式 Reads[A]
作为您提供的类型(在本例中为 Reads[Option[String]]
,不存在)。
相反,您需要使用 asOpt
,它将正确处理下面的 Option
:
scala> (jsonFoo \ "nextId").asOpt[String]
res1: Option[String] = None
nextId
没有出现在打印的 JSON 中,因为您正在序列化的值是 None
。这是预期会发生的事情。由于该值是可选的,因此它会从 JSON 中省略(它只是 JavaScript 中的 undefined
)。如果它有值,它会出现:
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 序列化,它就不会编译。
我有一个虚拟 class 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
对象时发现一些有趣的行为。如果我将 Foo
序列化为 JSON,解析 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.toJson(foo)
转储的 JSON 对象时,JSON 对象缺少 nextId
字段:
println(Json.prettyPrint(jsonFoo))
{
"id" : "id",
"fooType" : {
"a" : "a",
"b" : "b"
}
}
但是,我可以用 toOption
解析 nextId
字段;即,
assert((jsonFoo \ "nextId").toOption.isEmpty)
如果我的对象的其中一个成员本身不可反序列化,如何才能从 JSON 中正确解析我的对象?
nextId
字段是可序列化的,否则,您根本无法将 Foo
写入 JSON。
jsonFoo: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"}}
您遇到的问题是 Option[A]
没有 Reads
。选项由 Play JSON 专门处理。当使用 JSON 组合器时,我们使用 readNullable[A]
和 writeNullable[A]
而不是 read[Option[A]]
和 write[Option[A]]
。同样,当使用方法从 JsValue
中提取单个字段时,调用 as
将不起作用,因为它需要隐式 Reads[A]
作为您提供的类型(在本例中为 Reads[Option[String]]
,不存在)。
相反,您需要使用 asOpt
,它将正确处理下面的 Option
:
scala> (jsonFoo \ "nextId").asOpt[String]
res1: Option[String] = None
nextId
没有出现在打印的 JSON 中,因为您正在序列化的值是 None
。这是预期会发生的事情。由于该值是可选的,因此它会从 JSON 中省略(它只是 JavaScript 中的 undefined
)。如果它有值,它会出现:
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 序列化,它就不会编译。