使用 circe 预处理点符号样式字段
Use circe to preprocess dot-notation style fields
我有一些 json,其中包括一些字段,这些字段被扁平化为 bson-ish 格式,如 {"foo.bar" : "bash"}
。我想将其转换为以下表示 {"foo" : { "bar" : "bash"}}
并想知道我会在哪里进行这样的操作。使问题复杂化的是可能有多个这样的字段需要正确合并,例如{"foo.bar" : "a", "foo.bash" : "b", "foo.baz" : "c"}
-> {"foo" : { "bar" : "a", "bash" : "b", "baz" : "c"}}
.
这里是一个快速实现:
import io.circe.Json
val Dotted = "([^\.]*)\.(.*)".r
def expandDotted(j: Json): Json = j.arrayOrObject(
j,
js => Json.fromValues(js.map(expandDotted)),
_.toList.map {
case (Dotted(k, rest), v) => Json.obj(k -> expandDotted(Json.obj(rest -> v)))
case (k, v) => Json.obj(k -> expandDotted(v))
}.reduceOption(_.deepMerge(_)).getOrElse(Json.obj())
)
我还没有真正使用或详细测试过它,但它似乎有效:
scala> import io.circe.literal._
import io.circe.literal._
scala> val j1 = json"""{"foo.bar" : "a", "foo.bash" : "b", "foo.baz" : "c"}"""
j1: io.circe.Json =
{
"foo.bar" : "a",
"foo.bash" : "b",
"foo.baz" : "c"
}
scala> expandDotted(j1)
res1: io.circe.Json =
{
"foo" : {
"baz" : "c",
"bash" : "b",
"bar" : "a"
}
}
并且嵌套更深:
scala> expandDotted(json"""{ "x.y.z": true, "a.b": { "c": 1 } }""")
res2: io.circe.Json =
{
"a" : {
"b" : {
"c" : 1
}
},
"x" : {
"y" : {
"z" : true
}
}
}
并且只是为了确认它不会与无点键混淆:
scala> expandDotted(json"""{ "a.b": true, "x": 1 }""").noSpaces
res3: String = {"x":1,"a":{"b":true}}
请注意,在 "collisions" 的情况下(导致 JSON 对象和非对象 JSON 值,或多个非对象值的路径),行为是 Json#deepMerge
:
scala> expandDotted(json"""{ "a.b": true, "a": 1 }""").noSpaces
res4: String = {"a":1}
scala> expandDotted(json"""{ "a": 1, "a.b": true }""").noSpaces
res5: String = {"a":{"b":true}}
…这可能是您想要的,但在这些情况下您也可以让它失败,或者不扩展碰撞路径,或者做几乎任何您能想到的其他事情。
我有一些 json,其中包括一些字段,这些字段被扁平化为 bson-ish 格式,如 {"foo.bar" : "bash"}
。我想将其转换为以下表示 {"foo" : { "bar" : "bash"}}
并想知道我会在哪里进行这样的操作。使问题复杂化的是可能有多个这样的字段需要正确合并,例如{"foo.bar" : "a", "foo.bash" : "b", "foo.baz" : "c"}
-> {"foo" : { "bar" : "a", "bash" : "b", "baz" : "c"}}
.
这里是一个快速实现:
import io.circe.Json
val Dotted = "([^\.]*)\.(.*)".r
def expandDotted(j: Json): Json = j.arrayOrObject(
j,
js => Json.fromValues(js.map(expandDotted)),
_.toList.map {
case (Dotted(k, rest), v) => Json.obj(k -> expandDotted(Json.obj(rest -> v)))
case (k, v) => Json.obj(k -> expandDotted(v))
}.reduceOption(_.deepMerge(_)).getOrElse(Json.obj())
)
我还没有真正使用或详细测试过它,但它似乎有效:
scala> import io.circe.literal._
import io.circe.literal._
scala> val j1 = json"""{"foo.bar" : "a", "foo.bash" : "b", "foo.baz" : "c"}"""
j1: io.circe.Json =
{
"foo.bar" : "a",
"foo.bash" : "b",
"foo.baz" : "c"
}
scala> expandDotted(j1)
res1: io.circe.Json =
{
"foo" : {
"baz" : "c",
"bash" : "b",
"bar" : "a"
}
}
并且嵌套更深:
scala> expandDotted(json"""{ "x.y.z": true, "a.b": { "c": 1 } }""")
res2: io.circe.Json =
{
"a" : {
"b" : {
"c" : 1
}
},
"x" : {
"y" : {
"z" : true
}
}
}
并且只是为了确认它不会与无点键混淆:
scala> expandDotted(json"""{ "a.b": true, "x": 1 }""").noSpaces
res3: String = {"x":1,"a":{"b":true}}
请注意,在 "collisions" 的情况下(导致 JSON 对象和非对象 JSON 值,或多个非对象值的路径),行为是 Json#deepMerge
:
scala> expandDotted(json"""{ "a.b": true, "a": 1 }""").noSpaces
res4: String = {"a":1}
scala> expandDotted(json"""{ "a": 1, "a.b": true }""").noSpaces
res5: String = {"a":{"b":true}}
…这可能是您想要的,但在这些情况下您也可以让它失败,或者不扩展碰撞路径,或者做几乎任何您能想到的其他事情。