【发布时间】:2016-08-23 19:02:01
【问题描述】:
举个例子:
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,它会在对象具有正确结构时对其进行解码。
为此添加某种鉴别器字段是一个常见的建议,但我不想更改我已经使用 spray-json 和 json4s 生成/使用的 JSON。
在这些库中,您的编码器/解码器基本上只是PartialFunction[JValue, A] 和PartialFunction[A, JValue]。如果您的值未在域中定义,则说明失败。我认为这是一个非常简单、优雅的解决方案。除此之外,您还拥有 JSON 类型的提取器,因此很容易匹配字段/结构上的对象。
Rapture 更进一步,使字段顺序不重要并忽略不匹配字段的存在,因此您可以执行以下操作:
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 部分的代码也很混乱。除了在fold 的ifEmpty 一侧的额外类型转换之外,它与DecodeJson.||| 中的代码相同。
有没有人有更惯用的方法来为 argonaut 中的 Sum 类型编写基本编解码器,不需要需要鉴别器,而是可以匹配的 结构 json?
【问题讨论】: