2014-04-11 34 views
2

在Argonaut中,如果在case類包含Either的情況下,如何輕鬆地重命名相應的JSON屬性名稱。Argonaut.io:如何在包含任何類的情況下重命名json屬性Right/Left

例如,給出這樣的定義:

case class Foo(f: String) 
    case class Bar(b: String) 
    case class FooBar(e: Either[Foo, Bar]) 

    implicit def FooCodecJson: CodecJson[Foo] = casecodec1(Foo.apply, Foo.unapply)("f") 

    implicit def BarCodecJson: CodecJson[Bar] = casecodec1(Bar.apply, Bar.unapply)("b") 

    implicit def FooBarCodecJson: CodecJson[FooBar] = casecodec1(FooBar.apply, FooBar.unapply)("e") 

轉換FooBar以JSON像FooBar(Right(Bar("hello"))).asJson.spaces4結果如下:

{ 
    "e" : { 
     "Right" : { 
      "b" : "hello" 
     } 
    } 
} 

什麼是重命名「正確」的東西最簡單的方法在上面的輸出中更有意義? (我的實際情況有很多case類與許多Eithers,所以我在尋找可能的最簡潔的方式。)

回答

2

這裏有一個相當簡單的方法:

def renameFields(replacements: (JsonField, JsonField)*)(json: Json) = 
    json.withObject(obj => 
    replacements.foldLeft(obj) { 
     case (acc, (before, after)) => 
     acc(before).map(v => (acc - before) + (after, v)).getOrElse(acc) 
    } 
) 

def renamedEitherCodec[A, B](leftName: String, rightName: String)(implicit 
    ee: EncodeJson[Either[A, B]], 
    de: DecodeJson[Either[A, B]] 
) = CodecJson[Either[A, B]](
    e => renameFields("Left" -> leftName, "Right" -> rightName)(ee(e)), 
    c => de(c.withFocus(renameFields(leftName -> "Left", rightName -> "Right"))) 
) 

然後:

val fooOrBar = namedEitherCodec[Foo, Bar]("Foo", "Bar") 

implicit def FooBarCodecJson: CodecJson[FooBar] = casecodec1(
    FooBar.apply, FooBar.unapply 
)("e")(fooOrBar, fooOrBar) 

您也可以使fooOrBar隱式,但以這種方式重寫的類型實例往往被忽視。

相關問題