2017-02-26 3 views
2

は、私は3つのメンバを持つダミーのクラスFooを持っている:Fooオブジェクトをシリアル化するとき、私はいくつかの興味深い行動を見つける Option [String]型のメンバーがシリアル化されていないのはなぜですか?

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

として FooTypeが定義されている

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

。私はJSONにFooをシリアル化した場合、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オブジェクトを印刷すると、nextIdフィールドがJSONオブジェクトにありません:

println(Json.prettyPrint(jsonFoo)) 

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

ただし、nextIdフィールドはtoOptionで解析できます。つまり、

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

メンバの1つがネイティブに直列化できない場合、オブジェクトをJSONから正しく解析するにはどうすればよいですか。

答えて

2

nextIdフィールドはシリアル化可能です。そうでないと、FooをJSONにまったく書き込むことができませんでした。

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

あなたが抱えている問題はOption[A]のためのReadsが存在しないということです。オプションは、Play JSONによって特別に処理されます。 JSONコンビネータを使用する場合、read[Option[A]]write[Option[A]]の代わりにreadNullable[A]writeNullable[A]を使用します。同様に、メソッドを使用してJsValueから個々のフィールドを引き出す場合、asを呼び出しても、与えられた型の暗黙のReads[A]が必要です(この場合は存在しないReads[Option[String]])。

代わりに、あなたは正しくOptionの下を扱うなる、asOptを使用する必要があります:あなたがシリアライズされた値がNoneあるので

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

nextIdは、印刷された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"} 

何かがプレイJSONと、シリアライズしなかった場合、それは単にコンパイルされないでしょう。

+0

興味深い。 Playでは 'None'を' null'や 'JsNull'オブジェクトにシリアライズすると思うかもしれませんが、私は今カスタムライターを定義しなければならないことを見ています。答える時間をとってくれてありがとう! – erip

+0

これは片方向に過ぎません。 JSONの欠落したフィールドまたは 'null'は' Option'にマップされますが、 'Option'は2つのうちの一方にしかマッピングできません。デザイナーは、より合理的なこととしてフィールドを省略しました。 –

+0

意味があります。それが正しく解析できる限り、それはうまくいく - それは要点です。 – erip

関連する問題