I'm dealing with an API which expects a JSON object where one of the values (blob) is a JSON object stringified:
{
"credential": {
"blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}",
"project_id": "731fc6f265cd486d900f16e84c5cb594",
"type": "ec2",
"user_id": "bb5476fd12884539b41d5a88f838d773"
}
}
My domain class is:
case class Credential(access: String, secret: String, projectId: String, userId: String)
Encoding the domain class is easy:
implicit val encoder: Encoder[Credential] = (a: Credential) => Json.obj(
"type" -> "ec2".asJson,
"blob" -> Map("access" -> a.access, "secret" -> a.secret).asJson.noSpaces.asJson,
"project_id" -> a.projectId.asJson,
"user_id" -> a.userId.asJson
)
However decoding is much harder:
implicit val decoder: Decoder[Credential] = (c: HCursor) => for {
blobJsonString <- c.get[String]("blob")
blob <- decode[Json](blobJsonString).left.map(e => DecodingFailure(e.getMessage, c.downField("blob").history))
access <- blob.hcursor.get[String]("access")
secret <- blob.hcursor.get[String]("secret")
projectId <- c.get[String]("project_id")
userId <- c.get[String]("user_id")
} yield Credential(access, secret, projectId, userId)
I don't like this implementation because it forces me to depend on circe-parser, and to break the abstraction layer the Encoders/Decoders provide.
Is there a way to implement a Decoder which does double decoding in a general way?