Given a JSON string like this, how can I use Scala and Circe to parse the data into case classes:
val json = """{ "members": { "id1": { "name": "Foo" }, "id2": {"name": "bar" } } } """
case class Members(members: List[Member])
case class Member( id: String, name: String)
I have no control over the input JSON, but perhaps there is a way to use Circe to rewrite it to
{ "list": [ {"id": "id1", "name": "Foo"}, {"id": "id2", "name": "bar" } ] }
In which case the parsing should be possible with default decoders.
Note: the actual JSON has many more elements next to the name entry, in fact it is a nested structure (but there is no id element in there).
I have been able to parse it in the following way, but it involves tedious crafting of each decoder. I hope there is a more elegant way to achieve the goal?
import io.circe.Decoder
import io.circe.jawn.decode
val json = """{ "members": { "id1": { "name": "Foo" }, "id2": {"name": "bar" } } }"""
case class Member( id: String, name: String)
case class Members(members: List[Member])
implicit val MemberDecoder: Decoder[Member] = Decoder.instance { c =>
val nameCursor = c.downField("name")
val id = c.key.get
for {
name <- nameCursor.as[String]
} yield Member(id, name)
}
implicit val MembersDecoder: Decoder[Members] =
Decoder.instance { c =>
val membersC = c.downField("members")
val keys = membersC.keys.get
val members = keys
.map(k => {
membersC.get[Member](k)
}).toList
.map(_.right.get)
Right(Members( members))
}
val result = decode[Members](json)
result //val res0: Either[io.circe.Error,Members] = Right(Members(List(Member(id1,Foo), Member(id2,bar))))
scalaVersion := "2.13.7"val circeVersion = "0.14.1"members: Map[String, Member]turns out to be more practical than themembers: List[Member]