问题描述
我有一个案例类,其中一些逻辑约束在案例类主体中实现为 require
。当尝试从表示语法正确但逻辑上无效的实例的 JSON 中解码此 case 类时,异常实际上是在调用线程上抛出的,而不是作为 Left
中的 decode
返回。
重现代码片段:
case class Foo(bar: String) {
require(bar.length < 5)
}
object Sandbox extends App {
import io.circe.generic.auto._
import io.circe.parser._
val foo1 = decode[Foo](""" {"bar":"abc"} """)
println(s"foo1: $foo1")
//expected: Left(IllegalArgumentException("requirement failed"))
//actual: throws IllegalArgumentException("requirement failed")
val foo2 = decode[Foo](""" {"bar":"abcdefg"} """)
println(s"foo2: $foo2")
}
是否可以在不抛出的情况下将此异常作为 Left
从 decode
返回?欢迎任何想法/建议...
TIA
M.
解决方法
使用精炼自动派生这些编解码器并带有约束。
或者,如果您想验证 JSON 而不是构造函数,您可以随时调整编解码器,如
case class Foo(bar: String)
object Foo {
implicit val decoder: Decoder[Foo] = deriveDecoder[Foo].emapTry(foo =>
Try(require(foo.bar.length < 5))
)
}
有时你也可以使用中间类型进行派生和映射:
case class Foo(bar: String) {
require(bar.length < 5)
}
object Foo {
// allows usage of derivation,especially if you would derive several typeclasses
// and then adjust their behavior
private case class FooHelper(bar: String)
implicit val decoder: Decoder[Foo] = deriveDecoder[FooHelper].emapTry(helper =>
Try(Foo(helper.foo))
)
}
总的来说,我建议不要使用 require
,尤其是在构造函数中,而是使用智能构造函数。
sealed abstract case class Foo private (bar: String)
object Foo {
def parse(bar: String): Either[String,Foo] =
if (bar.length < 5) Right(new Foo(bar) {})
else Left(s"Invalid bar value: $bar")
def parseUnsafe(bar: String): Foo =
parse(bar).fold(error => throw new Exception(error),foo => foo)
private case class FooHelper(bar: String)
implicit val decoder: Decoder[Foo] = deriveDecoder[FooHelper].emapTry(helper =>
Try(parseUsafe(helper.foo))
)
}