播放[Scala]:如何展平JSON对象

j3d*_*j3d 4 json scala playframework

鉴于以下JSON ......

{
  "metadata": {
    "id": "1234",
    "type": "file",
    "length": 395
  }
}
Run Code Online (Sandbox Code Playgroud)

...我该如何将其转换为

{
  "metadata.id": "1234",
  "metadata.type": "file",
  "metadata.length": 395
}
Run Code Online (Sandbox Code Playgroud)

TX.

Tra*_*own 9

你可以用Play的JSON变换器简洁地完成这项工作.以下是我的头脑,我相信它可以大大改善:

import play.api.libs.json._

val flattenMeta = (__ \ 'metadata).read[JsObject].flatMap(
  _.fields.foldLeft((__ \ 'metadata).json.prune) {
    case (acc, (k, v)) => acc andThen __.json.update(
      Reads.of[JsObject].map(_ + (s"metadata.$k" -> v))
    )
  }
)
Run Code Online (Sandbox Code Playgroud)

然后:

val json = Json.parse("""
  {
    "metadata": {
      "id": "1234",
      "type": "file",
      "length": 395
    }
  }
""")
Run Code Online (Sandbox Code Playgroud)

和:

scala> json.transform(flattenMeta).foreach(Json.prettyPrint _ andThen println)
{
  "metadata.id" : "1234",
  "metadata.type" : "file",
  "metadata.length" : 395
}
Run Code Online (Sandbox Code Playgroud)

如果要处理metadata树中其他位置的字段,只需更改路径即可.


请注意,在这里使用变压器可能过度 - 请参阅例如Pascal Voitot在此主题中的输入,他在提出以下建议:

(json \ "metadata").as[JsObject].fields.foldLeft(Json.obj()) {
  case (acc, (k, v)) => acc + (s"metadata.$k" -> v)
}
Run Code Online (Sandbox Code Playgroud)

它不是可组合的,你可能不想as在实际代码中使用,但它可能就是你所需要的.


Mic*_*jac 8

这绝对不是微不足道的,但可以通过尝试递归地展平它.我没有对此进行彻底的测试,但它适用于您的示例以及我使用数组提出的其他一些基本功能:

object JsFlattener {

    def apply(js: JsValue): JsValue = flatten(js).foldLeft(JsObject(Nil))(_++_.as[JsObject])

    def flatten(js: JsValue, prefix: String = ""): Seq[JsValue] = {
        js.as[JsObject].fieldSet.toSeq.flatMap{ case (key, values) =>
            values match {
                case JsBoolean(x) => Seq(Json.obj(concat(prefix, key) -> x))
                case JsNumber(x) => Seq(Json.obj(concat(prefix, key) -> x))
                case JsString(x) => Seq(Json.obj(concat(prefix, key) -> x))
                case JsArray(seq) => seq.zipWithIndex.flatMap{ case (x, i) => flatten(x, concat(prefix, key + s"[$i]")) }  
                case x: JsObject => flatten(x, concat(prefix, key))
                case _ => Seq(Json.obj(concat(prefix, key) -> JsNull))
            }
        }
    }

    def concat(prefix: String, key: String): String = if(prefix.nonEmpty) s"$prefix.$key" else key

}
Run Code Online (Sandbox Code Playgroud)

JsObject有一个fieldSet方法返回一个Set[(String, JsValue)]我映射的,与JsValue子类匹配的方法,并从那里递归地继续消耗.

您可以通过传递JsValueapply:

val json = Json.parse("""
    {
      "metadata": {
        "id": "1234",
        "type": "file",
        "length": 395
      }
    }
"""
JsFlattener(json)
Run Code Online (Sandbox Code Playgroud)

我们将它作为练习留给读者,使代码看起来更漂亮.