json - 使用 Circe 展平嵌套的 JSON 对象
问题描述
假设我有一个这样的 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 频道的另一个常见问题解答。)
解决方案
您可以使用递归方法在 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 值,None
如果它是非 JSON 对象值,则返回,作为该字段不需要展平的信号,或者Some
在 JSON 对象的情况下包含一系列展平字段。
如果我们有这样的 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 对象的堆栈,但可能直到你有几千层深,所以在实践中不太可能成为问题。您可以在没有太多麻烦的情况下使其尾递归,但是对于只有几层嵌套的常见情况会产生额外的开销。