【问题标题】:Circe Decoder - Fallback to another decoder if failsCirce 解码器 - 如果失败则回退到另一个解码器
【发布时间】:2018-10-23 19:59:13
【问题描述】:

我正在使用 Circe 进行 json 操作。我添加了自定义编码器和解码器来处理一些类型,比如 Joda Time。

在解析 DateTime 时,我想允许传递多种格式。 例如。 dd-MM-yyyy'T'HH:mm:ss'Z'dd-MM-yyyy'T'HH:mm:ss.SSS'Z'

我已经定义了我的解码器,如下所示:

val dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
val dateTimeFormatWithMillis = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
implicit val jodaDateTimeFormat: Encoder[DateTime] with Decoder[DateTime] = new Encoder[DateTime] with Decoder[DateTime] {
    override def apply(a: DateTime): Json = Encoder.encodeString(a.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))

    override def apply(c: HCursor): Result[DateTime] = Decoder.decodeString.map { x =>
      DateTime.parse(x, dateTimeFormat)
    }.apply(c)
  }

现在如果我输入与dateTimeFormat 匹配的日期时间字符串,则解码将起作用,但如果我在dateTimeFormatWithMillis 中传递日期时间,它将无法处理。

我知道我可以使用DateTimeFormatterBuilder 添加多个解析器并对其进行处理,但是,我想知道 Circe 中是否有一种方法可以链接多个解码器以一个接一个地尝试,直到它成功或到达链的末尾?

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

    您可以使用Decoder#or 组合解码器,以便在第一个失败时尝试第二个。

    这是一个工作示例:

    import org.joda.time.DateTime
    import org.joda.time.format.{DateTimeFormat, DateTimeFormatter}
    import io.circe.{Decoder, Encoder}
    import io.circe.parser.decode
    import scala.util.Try
    
    
    val dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
    val dateTimeFormatWithMillis = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    
    
    /** Creates a decoder that decodes a [[DateTime]] using the provided format. */
    def dateTimeFormatDecoder(format: DateTimeFormatter): Decoder[DateTime] =
      Decoder[String].emapTry(str => Try(DateTime.parse(str, format)))
    
    /** [[Decoder]] for the first format (without milliseconds). */
    val dateTimeWithoutMillisDecoder: Decoder[DateTime] =
      dateTimeFormatDecoder(dateTimeFormat)
    
    /** [[Decoder]] for the second format (with milliseconds). */
    val dateTimeWithMillisDecoder: Decoder[DateTime] =
      dateTimeFormatDecoder(dateTimeFormatWithMillis)
    
    /** Encodes a [[DateTime]] using `Encoder[String].contramap(...)`, which is
      * perhaps a slightly more idiomatic version of 
      * `Encoder.encodeString(a.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))` */
    implicit val jodaDateTimeEncoder: Encoder[DateTime] =
      Encoder[String].contramap(_.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))
    
    implicit val jodaDateTimeDecoder: Decoder[DateTime] =
      dateTimeWithoutMillisDecoder or dateTimeWithMillisDecoder
    
    println(decode[DateTime](""" "2001-02-03T04:05:06Z" """))
    println(decode[DateTime](""" "2001-02-03T04:05:06.789Z" """))
    

    请注意,EncoderDecoder 已分开,因为 Decoder#or 返回一个 Decoder,这不适用于组合类(即 Encoder[DateTime] with Decoder[DateTime])。

    此外,DateTime.parse 调用已用 Decoder#emapTry 包装,因为 or 组合器(以及通常所有 Decoder 组合器)期望处理 Either 值,而不是异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      • 1970-01-01
      • 2017-11-27
      • 2017-06-12
      • 2019-07-01
      • 2019-01-26
      相关资源
      最近更新 更多