2017-10-17 42 views
0

考慮:爲密封的特徵層次結構編寫DecodeJson?

import argonaut._ 
import Argonaut._ 
import ArgonautShapeless._ 

sealed trait Parent 
case class Foo(x: Int) extends Parent 
case class Bar(y: String) extends Parent 

我試圖定義DecodeJson[Parent]

implicit val parentDecodeJson: DecodeJson[Parent] = 
    DecodeJson(c => c.focus.objectFields match { 
     case Some("x" :: _) => implicitly[DecodeJson[Foo]].decode(c) 
     case _    => implicitly[DecodeJson[Bar]].decode(c) 
    }) 

但是,失敗因爲argonaut.DecodeResult是不變的。

<console>:42: error: type mismatch; 
found : argonaut.DecodeResult[Foo] 
required: argonaut.DecodeResult[Parent] 
Note: Foo <: Parent, but class DecodeResult is invariant in type A. 
You may wish to define A as +A instead. (SLS 4.5) 
       case Some("x" :: _) => implicitly[DecodeJson[Foo]].decode(c) 
                    ^
<console>:43: error: type mismatch; 
found : argonaut.DecodeResult[Bar] 
required: argonaut.DecodeResult[Parent] 
Note: Bar <: Parent, but class DecodeResult is invariant in type A. 
You may wish to define A as +A instead. (SLS 4.5) 
      case _  => implicitly[DecodeJson[Bar]].decode(c) 
                  ^

所以,後來我想出了:

implicit val parentDecodeJson: DecodeJson[Parent] = 
    DecodeJson(c => c.focus.objectFields match { 
     case Some("x" :: _) => implicitly[DecodeJson[Foo]].decode(c).flatMap{a => DecodeResult.ok(a)} 
     case _  => implicitly[DecodeJson[Bar]].decode(c).flatMap{a => DecodeResult.ok(a)} 
    }) 

,似乎工作:

scala> Json.obj(("x", jNumber(42))).as[Parent] 
res2: argonaut.DecodeResult[Parent] = DecodeResult(Right(Foo(42))) 

scala> Json.obj(("y", jString("hi!"))).as[Parent] 
res3: argonaut.DecodeResult[Parent] = DecodeResult(Right(Bar(hi!))) 

有一個更清潔的方式?

+1

這應該是開箱即用的, 至少從README說的是什麼(https://github.com/alexarchambault/argonaut-shapeless) –

+0

這個怎麼樣:https://stackoverflow.com/questions/39108841 /解碼-A-密封特質在-淘金者基於上JSON結構 – tkachuko

回答

0

你並不需要定義自己的implicits:

println(EncodeJson.of[Parent].encode(Bar("a")))//{"Bar":{"y":"a"}} 
println(EncodeJson.of[Parent].apply(Bar("a")))//{"Bar":{"y":"a"}} 
println(Bar("a").asJson)//{"y":"a"} 
println((Bar("a"): Parent).asJson)//{"Bar":{"y":"a"}} 
println("{\"Bar\":{\"y\":\"a\"}}".decode[Parent])//Right(Bar(a)) 
println("{\"Bar\":{\"y\":\"a\"}}".decodeOption[Parent])//Some(Bar(a)) 

還是我誤解你的目標是什麼?