2016-08-23 35 views
1

考慮下面的例子:基於JSON結構解碼Argonaut中的密封特質?

sealed trait Id 

case class NewId(prefix: String, id: String) extends Id 
case class RevisedId(prefix: String, id: String, rev: String) extends Id 

case class User(key: Id, name: String) 

val json = """ 
{ 
    "key": { 
    "prefix": "user", 
    "id": "Rt01", 
    "rev": "0-1" 
    }, 
    "name": "Bob Boberson" 
} 
""" 

implicit val CodecUser: CodecJson[User] = casecodec2(User.apply, User.unapply)("key", "name") 

implicit val CodecId: CodecJson[Id] = ??? 

json.decodeOption[User] 

我需要編寫Id當它具有適當的結構,將解碼對象的CodecJson

添加某種歧視字段是一個常見的建議,但我不想更改我已經生產/使用的和json4s的JSON。

在那些庫中,您的編碼器/解碼器基本上只是PartialFunction[JValue, A]PartialFunction[A, JValue]。如果您的值未在域中定義,那就是失敗。這是一個非常簡單,優雅的解決方案,我認爲。除此之外,您還有JSON類型的提取器,因此可以很容易地在字段/結構上匹配對象。

被提進了一步,讓字段順序不重要,忽略不匹配字段的存在,所以你可能只是這樣做:

case json"""{ "prefix": $prefix, "id": $id, "rev": $rev }""" => 
    RevisedId(prefix, id, rev) 

這是很簡單/強大。

我無法弄清楚如何做與argonaut類似的工作。這是我迄今爲止最好的:

val CodecNewId = casecodec2(NewId.apply, NewId.unapply)("prefix", "id") 
val CodecRevisedId = casecodec3(RevisedId.apply, RevisedId.unapply)("prefix", "id", "rev") 

implicit val CodecId: CodecJson[Id] = 
    CodecJson.derived[Id](
    EncodeJson { 
     case id: NewId => CodecNewId(id) 
     case id: IdWithRev => RevisedId(id) 
    }, 
    DecodeJson[Id](c => { 
     val q = RevisedId(c).map(a => a: Id) 
     q.result.fold(_ => CodecNewId(c).map(a => a: Id), _ => q) 
    }) 
) 

所以有一些問題。我必須定義我不打算使用的額外編解碼器。我沒有在EncodeJson中爲CodecJson[Id]使用案例級提取器,而是委託給我定義的其他編碼器。對於只有2或3個字段的課程,只是感覺不太直接或不乾淨。

DecodeJson部分的代碼也很雜亂。除了foldifEmpty一側的其他類型外,它與DecodeJson.|||中的代碼相同。

沒有人有寫的總和,類型有一個基本的編解碼器中淘金是需要鑑別而是可以匹配在結構的JSON的更習慣的方法?

回答

1

這是我已經能夠想出最好的。它沒有部分功能所具有的優雅的基本感覺,但它比我的第一次嘗試更容易破譯。

val CodecNewId = casecodec2(NewId.apply, NewId.unapply)("prefix", "id") 
val CodecRevisedId = casecodec3(RevisedId.apply, RevisedId.unapply)("prefix", "id", "rev") 

implicit val CodecId: CodecJson[Id] = CodecJson(
    { 
    case id: NewId => CodecNewId(id) 
    case id: RevisedId => CodecRevisedId(id) 
    }, 
    (CodecRevisedId ||| CodecNewId.map(a => a: Id))(_)) 

我們仍然在爲每個子類型使用「具體」編解碼器。但是我們已經離開了CodecJson.derive的通話,我們不需要將我們的編碼功能包裝在EncodeJson中,我們可以用map我們的DecodeJson函數代替類型轉換,因此我們可以回到使用|||而不是複製它的實現,這使得代碼更具可讀性。

這絕對是一個可用的解決方案,如果不是我所希望的。