How to get better error messages when parsing JSON with Argonaut and custom codecs

413 Views Asked by At

I'm using Argonaut to parse JSON with my custom codecs. My code looks like this:

val json: String = ...
val parsed = Parse.decodeEither[MyClass](json)
val checks = if (parsed.isRight) parsed.right.get
else sys.error("Unable to parse MyClass json: " + parsed.left)

However, I never get any useful error messages. All I get is java.lang.RuntimeException: Unable to parse MyClass json: LeftProjection(Left(String: CursorHistory(List()))).

What is the proper way to handle decoding errors?

Edit: my question is not what to do with the provided error message. It would be great if Argonaut could say something like "Parsing error at position X, unexpected member y" or something like that. Is that possible?

2

There are 2 best solutions below

1
On

I'm not quite sure whether I understand what your actual question is...

You are getting Java.lang.RuntimeException: Unable to parse MyClass json: LeftProjection(Left(String: CursorHistory(List()))) because that's what sys.error does.

When you use the method Parse.decodeEither, the resulting type is an Either[String, MyClass]. There are many ways to check or operate with an Either. One of them, you are already using: Inspecting whether the result is on either side and act based on it.

A more idiomatic approach though, would be to fold over it:

parsed.fold(
    error => //do something with error,
    myclass => //do something with myclass
)

From Scala 2.12, Either is right biased Monad, which means that you can use it in a for comprehension that will flatMap over the right hand side. I think reading its docs could be of help.

1
On

I agree Argonaut decode errors can be a bit terse, but they are meaningful too. Pay attention to the CursorHistory object. It provides the most recent operation during decoding.

In your case, it looks like you're unable to parse the JSON since CursorHistory is an empty List.

Simple Example (not tested):

case class Person(name: String, age: Int)

object Person {
  implicit def PersonCodecJson: CodecJson[Person] =
    casecodec2(Person.apply, Person.unapply)("name", "age")
}

Person JSON:

{
  "name": "Fred"
}

The error parsing the example above would be something like: CursorHistory(List(El(CursorOpDownField(age),false))).