How do I ignore decoding failures in a JSON array?
The most straightforward way to solve this problem is to use a decoder that first tries to decode each value as a Foo
, and then falls back to the identity decoder if the Foo
decoder fails. The new either
method in circe 0.9 makes the generic version of this practically a one-liner:
import io.circe.{ Decoder, Json }def decodeListTolerantly[A: Decoder]: Decoder[List[A]] = Decoder.decodeList(Decoder[A].either(Decoder[Json])).map( _.flatMap(_.left.toOption) )
It works like this:
scala> val myTolerantFooDecoder = decodeListTolerantly[Foo]myTolerantFooDecoder: io.circe.Decoder[List[Foo]] = io.circe.Decoder$$anon$21@2b48626bscala> decode(badDoc)(myTolerantFooDecoder)res2: Either[io.circe.Error,List[Foo]] = Right(List(Foo(abc), Foo(xyz)))
To break down the steps:
Decoder.decodeList
says "define a list decoder that tries to use the given decoder to decode each JSON array value".Decoder[A].either(Decoder[Json]
says "first try to decode the value as anA
, and if that fails decode it as aJson
value (which will always succeed), and return the result (if any) as aEither[A, Json]
"..map(_.flatMap(_.left.toOption))
says "take the resulting list ofEither[A, Json]
values and remove all theRight
s".
…which does what we want in a fairly concise, compositional way. At some point we might want to bundle this up into a utility method in circe itself, but for now writing out this explicit version isn't too bad.