2017-02-26 147 views
2

我有一個虛擬的類Foo其中有三個成員:爲什麼不是Option [String]類型的成員被序列化?

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] 
} 

其中作爲

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對象時FooType定義。如果我序列化Foo到JSON,解析JSONified富,我發現,所有的成員都正確解析:

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" 
    } 
} 

但是,我可以解析nextId字段toOption;即

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

如果我的對象其中一個成員本身不能反序列化,我怎樣才能從JSON正確解析對象?

回答

2

nextId字段是可序列化的,否則,根本不可能寫入Foo到JSON。

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

您遇到的問題是,有沒有ReadsOption[A]。選項由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,因爲你是序列化的值None不會出現在打印JSON。這是預期會發生的事情。由於該值是可選的,因此它會從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進行序列化,它根本就不會編譯。

+0

有趣。我可能希望'None'在Play中被序列化爲'null'或'JsNull'對象,但我現在已經看到我必須爲此定義一個自定義作者。感謝您花時間回答! – erip

+0

它只能走一條路。 JSON中缺少的字段或'null'將被映射到'Option',但'Option'只能映射到其他方式之一。設計師選擇忽略該領域是更明智的做法。 –

+0

有道理。只要它能被正確解析,那就沒問題 - 這是關鍵所在。 – erip

相關問題