#json #scala #circe
#json #scala #circe
Вопрос:
У меня есть класс case с некоторыми логическими ограничениями, реализованными как require
s в теле класса case. При попытке декодировать этот класс case из JSON, представляющего синтаксически правильный, но логически недопустимый экземпляр, исключение фактически генерируется в вызывающем потоке, а не возвращается как Left
from 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
from decode
без выбрасывания? Любые идеи / предложения приветствуются…
TIA
M.
Комментарии:
1. Ну
require
, выдает исключения, так что это все. Также на практике никто его не использует. — Для этого варианта использования было бы лучше использовать уточненный тип.
Ответ №1:
Используйте refined для автоматического получения этих кодеков с ограничениями.
Или, если вы хотите проверить 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))
)
}