Play [Scala]: How to flatten a JSON object

后端 未结 6 1036
無奈伤痛
無奈伤痛 2021-02-06 13:07

Given the following JSON...

{
  \"metadata\": {
    \"id\": \"1234\",
    \"type\": \"file\",
    \"length\": 395
  }
}

... how do I convert it

6条回答
  •  北荒
    北荒 (楼主)
    2021-02-06 13:42

    You can do this pretty concisely with Play's JSON transformers. The following is off the top of my head, and I'm sure it could be greatly improved on:

    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))
        )
      }
    )
    

    And then:

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

    And:

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

    Just change the path if you want to handle metadata fields somewhere else in the tree.


    Note that using a transformer may be overkill here—see e.g. Pascal Voitot's input in this thread, where he proposes the following:

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

    It's not as composable, and you'd probably not want to use as in real code, but it may be all you need.

提交回复
热议问题