我正在使用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中是否有一种方法可以链接多个解码器,使其依次尝试直到成功或结束。的?
答案 0 :(得分:0)
您可以使用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" """))
请注意,Encoder
和Decoder
已分开,因为Decoder#or
返回了Decoder
,这不适用于组合类(即Encoder[DateTime] with Decoder[DateTime]
)。
此外,DateTime.parse
调用已被Decoder#emapTry
包装,因为or
组合器(通常是所有Decoder
组合器)希望处理{{1} }值,而不是例外。