假设我有一些如下的JSON数据:
{
"data": {
"title": "example input",
"someBoolean": false,
"innerData": {
"innerString": "input inner string",
"innerBoolean": true,
"innerCollection": [1,2,3,4,5]
},
"collection": [6,7,8,9,0]
}
}我想要将其展平一点,并转换或删除一些字段,以获得以下结果:
{
"data": {
"ttl": "example input",
"bool": false,
"collection": [6,7,8,9,0],
"innerCollection": [1,2,3,4,5]
}
}如何使用Circe执行此操作
(请注意,我将此作为常见问题来问,因为类似的问题经常出现在Circe Gitter channel中。这个具体的例子来自昨天在那里询问的一个question。)
发布于 2019-09-20 17:00:56
我有时会说,Circe主要是一个用于编码和解码JSON的库,而不是用于转换JSON值的库,通常我建议映射到Scala类型,然后定义这些类型之间的关系( Andriy Plokhotnyuk建议使用here),但在许多情况下,使用游标编写转换效果很好,在我看来,这类事情就是其中之一。
下面是我将如何实现这种转换:
import io.circe.{DecodingFailure, Json, JsonObject}
import io.circe.syntax._
def transform(in: Json): Either[DecodingFailure, Json] = {
val someBoolean = in.hcursor.downField("data").downField("someBoolean")
val innerData = someBoolean.delete.downField("innerData")
for {
boolean <- someBoolean.as[Json]
collection <- innerData.get[Json]("innerCollection")
obj <- innerData.delete.up.as[JsonObject]
} yield Json.fromJsonObject(
obj.add("boolean", boolean).add("collection", collection)
)
}然后:
val Right(json) = io.circe.jawn.parse(
"""{
"data": {
"title": "example input",
"someBoolean": false,
"innerData": {
"innerString": "input inner string",
"innerBoolean": true,
"innerCollection": [1,2,3]
},
"collection": [6,7,8]
}
}"""
)和:
scala> transform(json)
res1: Either[io.circe.DecodingFailure,io.circe.Json] =
Right({
"data" : {
"title" : "example input",
"collection" : [
6,
7,
8
]
},
"boolean" : false,
"collection" : [
1,
2,
3
]
})如果你以正确的方式看待它,我们的transform方法有点像一个解码器,我们实际上可以把它写成一个解码器(尽管我绝对不建议把它隐式地写出来):
import io.circe.{Decoder, Json, JsonObject}
import io.circe.syntax._
val transformData: Decoder[Json] = { c =>
val someBoolean = c.downField("data").downField("someBoolean")
val innerData = someBoolean.delete.downField("innerData")
(
innerData.delete.up.as[JsonObject],
someBoolean.as[Json],
innerData.get[Json]("innerCollection")
).mapN(_.add("boolean", _).add("collection", _)).map(Json.fromJsonObject)
}在某些情况下,当您希望将转换作为需要解码器的管道的一部分来执行时,这会很方便:
scala> io.circe.jawn.decode(myJsonString)(transformData)
res2: Either[io.circe.Error,io.circe.Json] =
Right({
"data" : {
"title" : "example input",
"collection" : [ ...不过,这也可能会让人感到困惑,我曾考虑向Circe添加某种Transformation类型,这样可以在不重新调整Decoder类型类的情况下封装这样的转换。
transform方法和这个解码器的一个优点是,如果输入数据没有预期的形状,那么产生的错误将包含指向问题的历史记录。
https://stackoverflow.com/questions/58025149
复制相似问题