json - 使用circe状态转换JSON

标签 json scala circe

注意:为了后代,我正在从circe Gitter channel复制此问题。

假设我们要翻译此JSON文档:

{
  "places": [{
    "id": "dadcc0d9-0615-4e46-9df4-2619f49930a0"
  }, {
    "id": "21d02f4b-7e88-47d7-bf2b-48e50761b6c3"
  }],
  "transitions": [{
    "id": "10a3aee5-541b-4d04-bb45-cb1dbbfe2128",
    "startPlaceId": "dadcc0d9-0615-4e46-9df4-2619f49930a0",
    "endPlaceId": "21d02f4b-7e88-47d7-bf2b-48e50761b6c3"
  }],
  "routes": [{
    "id": "6ded1763-86c0-44ce-b94b-f05934976a3b",
    "transitionId": "10a3aee5-541b-4d04-bb45-cb1dbbfe2128"
  }]
}

变成这个:
{
  "places": [{
    "id": "1"
  }, {
    "id": "2"
  }],
  "transitions": [{
    "id": "3",
    "startPlaceId": "ref:1",
    "endPlaceId": "ref:2"
  }],
  "routes": [{
    "id": "4",
    "transitionId": "ref:3"
  }]
}

也就是说,我们要用简单的递增数字标识符替换每个id中的UUID,并用对新标识符的引用替换对每个UUID的所有引用。

我们如何用circe做到这一点?

最佳答案

可以使用Cats(依赖于此的库)中的状态monad转换器相对简单地完成此操作:

import cats.data.StateT
import cats.std.option._
import cats.std.list._
import cats.syntax.traverse._
import io.circe.{ Json, JsonObject }
import java.util.UUID

def update(j: Json): StateT[Option, Map[UUID, Long], Json] = j.arrayOrObject(
  StateT.pure[Option, Map[UUID, Long], Json](j),
  _.traverseU(update).map(Json.fromValues),
  _.toList.traverseU {
    case ("id", value) => StateT { (ids: Map[UUID, Long]) =>
      value.as[UUID].toOption.map { uuid =>
        val next = if (ids.isEmpty) 1L else ids.values.max + 1L
        (ids.updated(uuid, next), "id" -> Json.fromString(s"$next"))
      }
    }
    case (other, value) => value.as[UUID].toOption match {
      case Some(uuid) => StateT { (ids: Map[UUID, Long]) =>
        ids.get(uuid).map(id => (ids, other -> Json.fromString(s"ref:$id")))
      }
      case None => update(value).map(other -> _)
    }
  }.map(Json.fromFields)
)

然后:
import io.circe.literal._

val doc: Json = json"""
{
  "places": [{
    "id": "dadcc0d9-0615-4e46-9df4-2619f49930a0"
  }, {
    "id": "21d02f4b-7e88-47d7-bf2b-48e50761b6c3"
  }],
  "transitions": [{
    "id": "10a3aee5-541b-4d04-bb45-cb1dbbfe2128",
    "startPlaceId": "dadcc0d9-0615-4e46-9df4-2619f49930a0",
    "endPlaceId": "21d02f4b-7e88-47d7-bf2b-48e50761b6c3"
  }],
  "routes": [{
    "id": "6ded1763-86c0-44ce-b94b-f05934976a3b",
    "transitionId": "10a3aee5-541b-4d04-bb45-cb1dbbfe2128"
  }]
}
"""

最后:
scala> import cats.std.long._
import cats.std.long._

scala> import cats.std.map._
import cats.std.map._

scala> update(doc).runEmptyA
res0: Option[io.circe.Json] = 
Some({
  "places" : [
    {
      "id" : "1"
    },
    {
      "id" : "2"
    }
  ],
  "transitions" : [
    {
      "id" : "3",
      "startPlaceId" : "ref:1",
      "endPlaceId" : "ref:2"
    }
  ],
  "routes" : [
    {
      "id" : "4",
      "transitionId" : "ref:3"
    }
  ]
})

如果任何id字段不是合法的UUID,或者任何其他字段包含对未知UUID的引用,则计算将失败,并显示None。可以根据需要对此行为进行一些细化,并且如果您想获得有关错误发生位置的更多具体信息,则可以调整实现以使其适用于游标而不是JSON值(但这会变得更加复杂)。

关于json - 使用circe状态转换JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36605768/

相关文章:

scala - 是否等待 Scala Future 阻塞线程?

scala - 认证路由中的 http4s json 处理

json - 在使用 circe 解码 JSON 对象时捕获未使用的字段

c# - 如何在代码中写入 JSON 字符串值?

java - Gremlin 通过 java 读取外部 JSON 顶点并添加到现有开放图给出了 Invalid vertexprovided 异常?

c# - 如何从 jQuery ajax 调用将复杂对象传递给 ASP.NET WebApi GET?

Scala 映射方法未按预期运行

scala - Shapeless - 将一个案例类转换为另一个具有不同顺序字段的案例类

scala - 在Scala中使用circe在一行中打印json字符串

php - Laravel REST API 和高 CPU 负载