Kevin Meredith -4 years ago 209
Scala Question

# `circe` Type-level Json => A Function?

Using

`circe`
or
`argonaut`
, how can I write a
`Json => A`
(note -
`Json`
may not be the name of the type) where
`A`
is given by the
`SSN`
class:

``````  // A USA Social Security Number has exactly 8 digits.
case class SSN(value: Sized[List[Nat], _8])
``````

?

Pseudocode:

`// assuming this function is named f`

`f(JsArray(JsNumber(1)))`
would fail to become an
`A`
since its size is 1, whereas

`f(JsArray(JsNumber(1), ..., JsNumber(8)))`
===
`SSN(SizedList(1,...,8))`

circe doesn't (currently) provide instances for `Sized`, but it probably should. In any case you can write your own pretty straightforwardly:

``````import cats.data.Xor
import io.circe.{ Decoder, DecodingFailure }
import shapeless.{ Nat, Sized }
import shapeless.ops.nat.ToInt
import shapeless.syntax.sized._

implicit def decodeSized[L <: Nat, A](implicit
dl: Decoder[List[A]],
ti: ToInt[L]
): Decoder[Sized[List[A], L]] = Decoder.instance { c =>
dl(c).flatMap(as =>
Xor.fromOption(as.sized[L], DecodingFailure(s"Sized[List[A], _\${ti()}]", c.history))
)
}
``````

I've restricted this to `List` representations, but you could make it more generic if you wanted.

Now you can write your `SSN` instance like this (note that I'm using `Int` instead of `Nat` for the individual numbers, since once you've got something statically typed as a `Nat` it's not worth much):

``````case class SSN(value: Sized[List[Int], Nat._8])

implicit val decodeSSN: Decoder[SSN] = Decoder[Sized[List[Int], Nat._8]].map(SSN(_))
``````

And then:

``````scala> import io.circe.jawn.decode
import io.circe.jawn.decode

scala> decode[SSN]("[1, 2, 3, 4, 5, 6, 7, 8]")
res0: cats.data.Xor[io.circe.Error,SSN] = Right(SSN(List(1, 2, 3, 4, 5, 6, 7, 8)))

scala> decode[SSN]("[1, 2, 3, 4, 5, 6, 7]")
res1: cats.data.Xor[io.circe.Error,SSN] = Left(DecodingFailure(Sized[List[A], _8], List()))
``````

If you really want a `Json => SSN` you could do this:

``````val f: Json => SSN = Decoder[SSN].decodeJson(_).valueOr(throw _)
``````

But that's not really idiomatic use of circe.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download