如何在Circe中创建用于解析时间值的自定义解码器

时间:2019-04-26 01:52:02

标签: json scala json-deserialization circe

我正在尝试将格式为“ 5m”,“ 5s”或“ 5ms”的字符串解码为FiniteDuration类型的对象,这些对象分别为5分钟,5秒,5毫秒。

我正在尝试为涉及FiniteDuration类的项目创建自定义解码器和编码器。编码器没问题,因为它只是读取FiniteDuration类的字段并生成一个字符串。但是,我在编写解码器时遇到困难,并且想知道我所做的事情是否完全可能。

FiniteDuration是一个具有如下构造函数的类:FiniteDuration(length:Long,unit:TimeUnit)。 Scala附带了一些方便的语法糖,因此可以使用5分钟,5秒或5毫秒表示法来调用该类。在这种情况下,Scala会为您创建FiniteDuration类。

这个想法是将FiniteDuration类转换为字符串,例如“ 5m”,“ 5s”或“ 5ms”,这样在眼睛上就更容易了。

  implicit val d2json: Encoder[FiniteDuration] = new Encoder[FiniteDuration] {
    override def apply(a: FiniteDuration): Json = ???
  }

  implicit val json2d: Decoder[FiniteDuration] = new Decoder[FiniteDuration] {
    override def apply(c: HCursor): Decoder.Result[FiniteDuration] = ???
  }

编码器我应该没有问题。解码器比较棘手。我不确定该怎么做,因为apply方法需要输入HCursor类型。

3 个答案:

答案 0 :(得分:2)

我想您希望您的解析器符合HOCON标准吗? 然后,您可以重复使用或复制com.typesafe.config库中使用的解析器。您需要的方法是

public static long parseDuration(String input, ConfigOrigin originForException, String pathForException)

答案 1 :(得分:2)

这是一个有效的基本实现(可能需要根据您对FiniteDuration的编码方式进行调整。

基本上,您需要做的是将光标的值设为String,将字符串分成持续时间和句点,然后尝试将这两个部分都转换为Long和{{1} }(因为TimeUnit构造函数接受它们作为参数)。

请注意,这些转换必须返回FiniteDuration才能与Either[DecodingFailure, _]的返回类型保持一致,因此您可以在理解中使用它们。

我对这些转换使用了隐式扩展方法,因为我发现它们很方便,但是您可以编写基本功能。

cursor.as[_]

答案 2 :(得分:0)

我认为解码 CREATE TABLE user_to_sub_user ( user_id int, sub_user_id int ); 的更好方法是使用现有的类 FiniteDuration 并且它是从 std lib 解析的:

scala.concurrent.Duration

我从 import io.circe.parser.decode import io.circe.{ CursorOp, Decoder, DecodingFailure, HCursor } import cats.syntax.validated._ import cats.data.Validated import scala.concurrent.duration.{ Duration, FiniteDuration } import scala.language.postfixOps import scala.util.Try import scala.concurrent.duration._ def parseDuration(ops: => List[CursorOp]) (d: String): Either[DecodingFailure, FiniteDuration] = Validated .fromTry(Try(Duration(d))) .andThen { case _: Duration.Infinite => new Exception("Field can not be infinite") .invalid[FiniteDuration] case duration: FiniteDuration => duration.valid[Throwable] } .leftMap(DecodingFailure.fromThrowable(_, ops)) .toEither implicit val fDurationDecoder: Decoder[FiniteDuration] = (c: HCursor) => c.as[String].flatMap(parseDuration(c.history)) 添加了 Validated 只是为了更舒适的错误处理并添加对无限输入的验证

cats