注意:为了后代,我在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执行此操作?
答案 0 :(得分:10)
使用来自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值(但这会更复杂一些)。