如何使用 Circe 将具有键值对象的 JSON 对象解析为列表
How can I parse a JSON object with key-value objects into a List using Circe
给定一个像这样的 JSON 字符串,我如何使用 Scala 和 Circe 将数据解析为大小写 类:
val json = """{ "members": { "id1": { "name": "Foo" }, "id2": {"name": "bar" } } } """
case class Members(members: List[Member])
case class Member( id: String, name: String)
我无法控制输入 JSON,但也许有办法使用 Circe 将其重写为
{ "list": [ {"id": "id1", "name": "Foo"}, {"id": "id2", "name": "bar" } ] }
在这种情况下,应该可以使用默认解码器进行解析。
注意:实际的JSON在name
条目旁边有更多的元素,实际上它是一个嵌套结构(但那里没有id
元素)。
我已经能够通过以下方式解析它,但是它涉及到每个解码器的繁琐制作。希望有更优雅的方式实现目标?
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))))
您不需要编写自定义 Member
解码器。您可以在解码之前修改 json 。试试像
(membersC.get(k) :+ ("id", k)).as[Member]
这可能不是完全正确的语法(我对 circe 不是很熟悉)但希望你明白了。
经过更多的研究和实验,我想到了这个(仍然有一些代码,但我想从键->值映射到(键+值)列表的切换并不简单。
import io.circe.generic.semiauto.deriveDecoder
import io.circe.jawn.decode
import io.circe.{ACursor, Decoder, Json}
val jsonMember = """{"id":"A", "name":"Aname"}"""
val jsonMemberNoID = """{"name":"Aname"}"""
val json =
"""{ "members": { "id1": { "name": "Foo" }, "id2": {"name": "bar" }, "id3": {"id":"ID3", "name":"baz"} } }"""
case class Member(id: String, name: String)
case class Members(members: List[Member])
implicit val MemberDecoder: Decoder[Member] = deriveDecoder[Member].prepare { (aCursor: ACursor) => {
aCursor.withFocus(json => {
json.mapObject(jsonObject =>{
if (!jsonObject.contains("id")){
jsonObject.add("id", Json.fromString(aCursor.key.getOrElse("?")))
} else {
jsonObject
}
})
})
}}
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(_.toOption.get)
Right(Members(members))
}
val memberResult = decode[Member](jsonMember)
val result = decode[Members](json)
val m2 = decode[Member](jsonMemberNoID)
最后 3 行在工作表中生成此内容:
val memberResult: Either[io.circe.Error,Member] = Right(Member(A,Aname))
val result: Either[io.circe.Error,Members] = Right(Members(List(Member(id1,Foo), Member(id2,bar), Member(ID3,baz))))
val m2: Either[io.circe.Error,Member] = Right(Member(?,Aname))
给定一个像这样的 JSON 字符串,我如何使用 Scala 和 Circe 将数据解析为大小写 类:
val json = """{ "members": { "id1": { "name": "Foo" }, "id2": {"name": "bar" } } } """
case class Members(members: List[Member])
case class Member( id: String, name: String)
我无法控制输入 JSON,但也许有办法使用 Circe 将其重写为
{ "list": [ {"id": "id1", "name": "Foo"}, {"id": "id2", "name": "bar" } ] }
在这种情况下,应该可以使用默认解码器进行解析。
注意:实际的JSON在name
条目旁边有更多的元素,实际上它是一个嵌套结构(但那里没有id
元素)。
我已经能够通过以下方式解析它,但是它涉及到每个解码器的繁琐制作。希望有更优雅的方式实现目标?
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))))
您不需要编写自定义 Member
解码器。您可以在解码之前修改 json 。试试像
(membersC.get(k) :+ ("id", k)).as[Member]
这可能不是完全正确的语法(我对 circe 不是很熟悉)但希望你明白了。
经过更多的研究和实验,我想到了这个(仍然有一些代码,但我想从键->值映射到(键+值)列表的切换并不简单。
import io.circe.generic.semiauto.deriveDecoder
import io.circe.jawn.decode
import io.circe.{ACursor, Decoder, Json}
val jsonMember = """{"id":"A", "name":"Aname"}"""
val jsonMemberNoID = """{"name":"Aname"}"""
val json =
"""{ "members": { "id1": { "name": "Foo" }, "id2": {"name": "bar" }, "id3": {"id":"ID3", "name":"baz"} } }"""
case class Member(id: String, name: String)
case class Members(members: List[Member])
implicit val MemberDecoder: Decoder[Member] = deriveDecoder[Member].prepare { (aCursor: ACursor) => {
aCursor.withFocus(json => {
json.mapObject(jsonObject =>{
if (!jsonObject.contains("id")){
jsonObject.add("id", Json.fromString(aCursor.key.getOrElse("?")))
} else {
jsonObject
}
})
})
}}
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(_.toOption.get)
Right(Members(members))
}
val memberResult = decode[Member](jsonMember)
val result = decode[Members](json)
val m2 = decode[Member](jsonMemberNoID)
最后 3 行在工作表中生成此内容:
val memberResult: Either[io.circe.Error,Member] = Right(Member(A,Aname))
val result: Either[io.circe.Error,Members] = Right(Members(List(Member(id1,Foo), Member(id2,bar), Member(ID3,baz))))
val m2: Either[io.circe.Error,Member] = Right(Member(?,Aname))