解码基于JSON结构的Argonaut中的密封特征? [英] Decoding a sealed trait in Argonaut based on JSON structure?

查看:120
本文介绍了解码基于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,以便在对象具有适当结构时对其进行解码.

I need to write a CodecJson for Id that will decode an object when it has the proper structure.

为此通常建议添加某种鉴别符字段,但我不想更改已经使用spray-jsonjson4s生成/使用的JSON.

Adding a discriminator field of some sort is a common suggestion for this, but I don't want to change the JSON I'm already producing/consuming with spray-json and json4s.

在这些库中,您的编码器/解码器基本上只是PartialFunction[JValue, A]PartialFunction[A, JValue].如果您的值未在域中定义,那就失败了.我认为这是一个非常简单,优雅的解决方案.除此之外,您还可以使用JSON类型的提取器,因此很容易在字段/结构上匹配对象.

In those libraries your encoders/decoders are basically just PartialFunction[JValue, A] and PartialFunction[A, JValue]. If your value isn't defined in the domain it's a failure. It's a really simple, elegant solution I think. In addition to that you've got Extractors for the JSON types, so it's easy to match an object on fields/structure.

抢劫又走了一步,使字段顺序变得不重要,并且忽略了不匹配字段的存在,因此您可以执行以下操作:

Rapture goes a step further, making field order unimportant and ignoring the presence of non-matching fields, so you could just do something like:

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

那真的很简单/功能强大.

That's really simple/powerful.

我在弄清楚如何使用argonaut做类似的事情时遇到了麻烦.这是到目前为止我能想到的最好的方法:

I'm having trouble figuring out how to do something similar with argonaut. This is the best I've come up with so far:

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个字段的类,只是感觉不太简单或干净.

So there's a few problems with that. I have to define extra codecs I don't intend to use. Instead of using the case-class extractors in the EncodeJson for CodecJson[Id], I'm delegating to the other encoders I've defined. Just just doesn't feel very straight-forward or clean for classes that only have 2 or 3 fields.

DecodeJson部分的代码也很混乱.除了foldifEmpty侧的额外类型转换外,它与DecodeJson.|||中的代码相同.

The code for the DecodeJson section is also pretty messy. Aside from an extra type-cast in the ifEmpty side of the fold it's identical to the code in DecodeJson.|||.

有人能用更惯用的方式在argonaut中为Sum类型编写基本编解码器吗,而不需要则需要鉴别符,而可以在结构上进行匹配json?

Does anyone have a more idiomatic way to write a basic codec for Sum-types in argonaut that doesn't require a discriminator and can instead match on the structure of the json?

推荐答案

这是我能想到的最好的方法.局部函数没有那种基本的优雅感觉,但是比起我的第一次尝试,它更简洁,更容易破译.

This is the best I've been able to come up with. It doesn't have that fundamental feel of elegance that the partial functions do, but it is a good bit more terse and easier to decipher than my first attempt.

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函数而不是类型转换,因此我们可以返回使用|||而不是复制其实现,这使代码更具可读性.

We're still using the "concrete" codecs for each sub-type. But we've gotten away from the CodecJson.derive call, we don't need to wrap our encode function in an EncodeJson, and we can map our DecodeJson function instead of type-casting so we can go back to using ||| instead of copying it's implementation, which makes the code a lot more readable.

如果不是我希望的那样,这绝对是一个可用的解决方案.

This is definitely a usable solution, if not quite what I'd hoped for.

这篇关于解码基于JSON结构的Argonaut中的密封特征?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆