При декодировании сообщения с помощью Circe возможно ли извлечь недопустимое значение из DecodingFailure - PullRequest
1 голос
/ 02 апреля 2020

При попытке декодировать сообщение json с помощью circe у меня есть требование вернуть исходное значение, которое вызвало ошибку декодирования, если это возможно.

В качестве примера я получаю некоторые invalidJson, которые содержит invalidUuid. Имея доступ к invalidJson и history: List[CursorOp], как бы я взял invalidValue?

Пример кода, иллюстрирующий проблему:

import java.util.UUID

import io.circe.{CursorOp, DecodingFailure}
import org.scalatest.{FlatSpec, Matchers}
import cats.implicits._
import io.circe.generic.auto._

class JsonSpec extends FlatSpec with Matchers {
  case class TestDecode(test: Test)
  case class Test(uuidKey: Option[UUID])

  "a token decoder" should "return the invalid value for a decoding failure" in {
    val invalidUuid = "invalid"
    val invalidJson = s"""{"test": {"uuidKey": "$invalidUuid"}}"""

    io.circe.parser.decode[TestDecode](invalidJson) match {
      case Left(DecodingFailure(_, history)) =>
        getInvalidValue(invalidJson, history) shouldEqual invalidUuid.some
      case Left(_) => fail("should have returned a DecodingFailure")
      case Right(_) => fail("should have returned a DecodingFailure")

    }

    def getInvalidValue(invalidJson: String, history: List[CursorOp]): Option[String] = ???
  }
}```

1 Ответ

1 голос
/ 02 апреля 2020

Да, это именно тот случай использования, который replay предназначен для поддержки:

import io.circe.{CursorOp, Decoder, DecodingFailure}
import io.circe.generic.auto._
import io.circe.jawn.parse
import java.util.UUID

case class TestDecode(test: Test)
case class Test(uuidKey: Option[UUID])

val Right(doc) = parse("""{"test": {"uuidKey": "invalid"}}""")
val Left(DecodingFailure(_, ops)) = Decoder[TestDecode].decodeJson(doc)

doc.hcursor.replay(ops).focus
// res0: Option[io.circe.Json] = Some("invalid")

Это метод на ACursor, который берет список операций и применяет их к курсору. (в обратном порядке, поскольку операции обычно собираются в виде стека, но в этом случае мы хотим использовать FIFO).

Обратите внимание, что focus - это Option, потому что библиотека не имеет никакого состояния c гарантирует, что вы применяете этот список операций к тому же значению JSON, которое вы изначально пытались декодировать, поэтому он может потерпеть неудачу.

...