首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Circe转换JSON

用Circe转换JSON
EN

Stack Overflow用户
提问于 2019-09-20 17:00:56
回答 1查看 623关注 0票数 3

假设我有一些如下的JSON数据:

代码语言:javascript
复制
{
    "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]
    }
}

我想要将其展平一点,并转换或删除一些字段,以获得以下结果:

代码语言:javascript
复制
{
    "data": {
        "ttl": "example input",
        "bool": false,
        "collection": [6,7,8,9,0],
        "innerCollection": [1,2,3,4,5]
    }
}

如何使用Circe执行此操作

(请注意,我将此作为常见问题来问,因为类似的问题经常出现在Circe Gitter channel中。这个具体的例子来自昨天在那里询问的一个question。)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-09-20 17:00:56

我有时会说,Circe主要是一个用于编码和解码JSON的库,而不是用于转换JSON值的库,通常我建议映射到Scala类型,然后定义这些类型之间的关系( Andriy Plokhotnyuk建议使用here),但在许多情况下,使用游标编写转换效果很好,在我看来,这类事情就是其中之一。

下面是我将如何实现这种转换:

代码语言:javascript
复制
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)
  )
}

然后:

代码语言:javascript
复制
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]
    }
  }"""
)

和:

代码语言:javascript
复制
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方法有点像一个解码器,我们实际上可以把它写成一个解码器(尽管我绝对不建议把它隐式地写出来):

代码语言:javascript
复制
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)
}

在某些情况下,当您希望将转换作为需要解码器的管道的一部分来执行时,这会很方便:

代码语言:javascript
复制
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方法和这个解码器的一个优点是,如果输入数据没有预期的形状,那么产生的错误将包含指向问题的历史记录。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58025149

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档