我已经在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 :好的,已经回答了,所以我不会删除它。保持坚强,神秘的问题,保持坚强...
答案 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))
}
这还有一个好处,就是不必担心隐式优先级和类似的东西。可能会简化您的解码器。