假设我有一个像这样的 JSON 对象:
{
"foo": true,
"bar": {
"baz": 1,
"qux": {
"msg": "hello world",
"wow": [null]
}
}
}
我想将其递归地展平为单层,并将键与下划线合并:
{
"foo": true,
"bar_baz": 1,
"baz_qux_msg": "hello world",
"baz_qux_wow": [null]
}
如何使用 Circe 执行此操作?
(注意:这是 Circe Gitter channel 的另一个常见问题解答。)
最佳答案
您可以使用递归方法在 Circe 中轻松完成此操作:
import io.circe.Json
def flatten(combineKeys: (String, String) => String)(value: Json): Json = {
def flattenToFields(value: Json): Option[Iterable[(String, Json)]] =
value.asObject.map(
_.toIterable.flatMap {
case (k, v) => flattenToFields(v) match {
case None => List(k -> v)
case Some(fields) => fields.map {
case (innerK, innerV) => combineKeys(k, innerK) -> innerV
}
}
}
)
flattenToFields(value).fold(value)(Json.fromFields)
}
这里,我们的内部 flattenToFields
方法接受每个 JSON 值,如果它是非 JSON 对象值,则返回 None
,作为该字段不需要展平的信号,或者在 JSON 对象的情况下包含一系列扁平化字段的 Some
。
如果我们有这样的 JSON 值:
val Right(doc) = io.circe.jawn.parse("""{
"foo": true,
"bar": {
"baz": 1,
"qux": {
"msg": "hello world",
"wow": [null]
}
}
}""")
我们可以验证 flatten
是否达到了我们想要的效果:
scala> flatten(_ + "_" + _)(doc)
res1: io.circe.Json =
{
"foo" : true,
"bar_baz" : 1,
"bar_qux_msg" : "hello world",
"bar_qux_wow" : [
null
]
}
请注意,flattenToFields
不是尾递归,对于深度嵌套的 JSON 对象,它会溢出堆栈,但可能直到深度达到数千层时才会溢出,因此这不太可能成为问题实践。您可以使其尾部递归,而不会有太多麻烦,但在只有几层嵌套的常见情况下,会产生额外的开销。
关于json - 使用 Circe 展平嵌套 JSON 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58026172/