如何在Circe中为[Option [Option [A]]编写自定义解码器?

时间:2018-09-20 19:15:48

标签: scala circe

我已经在play-json中为Reads编写了一个Option[Option[A]]转换器,其行为如下:

//given this case class
case class MyModel(field: Option[Option[String]])

//this JSON -- maps to --> this MyModel:
//"{ \"field\": \"value\" }"  -->  MyModel(field = Some(Some("value")))
//"{ \"field\": null, ...  }"   -->  MyModel(field = Some(None))
//"{ }"  -->  MyModel(field = None)

因此,提供映射到Some[Some[A]]的值,提供映射到null的{​​{1}}(即Some[None]),而不是仅映射到{{1} }(即Some[Option.empty[A]])。这是play-json转换器:

None

现在,我正在将play-json代码转换为Circe,但我不知道如何编写具有相同行为的Option.empty[Option[A]]。也就是说,我需要

def readOptOpt[A](implicit r: Reads[A]): Reads[Option[Option[A]]] = {
  Reads[Option[Option[A]]] { json =>
    path.applyTillLast(json).fold(
      identity,
      _.fold(_ => JsSuccess(None), {
        case JsNull => JsSuccess(Some(None))
        case js => r.reads(js).repath(path).map(a => Some(Some(a)))
      })
    )
  }
}

关于如何进行这项工作的任何想法?谢谢

我知道了这一点:

有两个问题:

1)如何处理JSON字段完全丢失的情况。事实证明,您必须遵循Circe的Decoder[Option[Option[A]]代码,在自定义解码器中使用def optOptDecoder[A](implicit d: Decoder[A]): Decoder[Option[Option[A]] = ??? //help!

2)当解码器代码位于帮助对象(或任何位置)中时,如何使编译器识别Decoder.reattempt的情况。事实证明,如果您使用的是半自动派生,则可以在随播对象中创建一个隐式对象,它将隐式覆盖默认值:

decodeOption

无论如何,我认为这对将来的任何人都不会有太大帮助,因此,除非我在接下来的几个小时内获得任何支持,否则我将删除它。

Edit2 :好的,已经回答了,所以我不会删除它。保持坚强,神秘的问题,保持坚强...

1 个答案:

答案 0 :(得分:2)

Option[Option[A]]有点奇怪。我理解并大体上同意这种推理,但是我认为这很奇怪,以至于它可能只需要用您自己的类替换(并为此编写一个解码器)就可以了。像这样:

sealed trait OptionalNull[+A] {
  def toOption: Option[Option[A]]
}
object NotPresent extends OptionalNull[Nothing] {
  override def toOption = None
}
object PresentButNull extends OptionalNull[Nothing] {
  override def toOption = Some(None)
}
case class PresentNotNull[A](value: A) extends OptionalNull[A] {
  override def toOption = Some(Some(value))
}

这还有一个好处,就是不必担心隐式优先级和类似的东西。可能会简化您的解码器。