使用circe或argonaut,我怎么能写一个Json => A(注意 – Json可能不是该类型的名称)其中A由SSN类给出:
// A USA Social Security Number has exactly 8 digits. case class SSN(value: Sized[List[Nat],_8])
?
伪代码:
//假设这个函数名为f
f(JsArray(JsNumber(1)))因为它的大小为1而不能成为A,而
f(JsArray(JsNumber(1),…,JsNumber(8)))=== SSN(SizedList(1,8))
解决方法
circe(当前)没有为Sized提供实例,但它可能应该.在任何情况下,你都可以直截了当地写自己的:
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)) ) }
我已将此限制为List表示,但如果您愿意,可以使其更通用.
现在你可以像这样编写你的SSN实例(请注意,我使用的是Int而不是Nat用于个别数字,因为一旦你得到一些静态类型为Nat的东西,它就不值得了):
case class SSN(value: Sized[List[Int],Nat._8]) implicit val decodeSSN: Decoder[SSN] = Decoder[Sized[List[Int],Nat._8]].map(SSN(_))
然后:
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,8))) scala> decode[SSN]("[1,7]") res1: cats.data.Xor[io.circe.Error,SSN] = Left(DecodingFailure(Sized[List[A],_8],List()))
如果你真的想要一个Json => SSN你可以这样做:
val f: Json => SSN = Decoder[SSN].decodeJson(_).valueOr(throw _)
但这并不是对circe的惯用.