TAGS :Viewed: 2 - Published at: a few seconds ago

[ Play 2.4.1: How to replace all the occurrences of one or more keys in a JSON tree ]

Given the following JSON...

{
  "id" : "52fe942b790000790079b7d0",
  "email" : "joe@domain.com",
  "username" : "joe",
  "subscriptions" : [
    {
      "accountId" : "72fe942b790000790079b755",
      "name" : "test 1",
      "isDefault" : true
    },
    {
      "accountId" : "72fe942b796850790079b743",
      "name" : "test 2",
      "isDefault" : false
    }
  ]
}

.. I need to transform each id to an ObjectID as required by MongoDB (i.e. id -> _id \ $oid and accountId -> accountId \ $oid:

{
  "_id" : {"$oid" : "52fe942b790000790079b7d0"},
  "email" : "joe@domain.com",
  "username" : "joe",
  "subscriptions" : [
    {
      "accountId" : {"$oid" : "72fe942b790000790079b755"},
      "name" : "test 1",
      "isDefault" : true
    },
    {
      "accountId" : {"$oid" : "72fe942b796850790079b743"},
      "name" : "test 2",
      "isDefault" : false
    }
  ]
}

Up to Play 2.3.8 I used play-json-zipper and updateAllKeyNodes did the trick:

import play.api.libs.json._
import play.api.libs.json.extensions._

json.updateAllKeyNodes {
  case ((_ \ "_id"), value) => "id" -> value \ "$oid"
  case ((_ \ "accountId"), value) => "accountId" -> value \ "$oid"
}

Unfortunately with play 2.4.1 I have to remove play-json-zipper from my project because it doesn't support the new JSON model.

What is the correct alternative to achieve the same result with the standard Play JSON library?

Answer 1


You can use - and + with JsObject.

val json = Json.parse("""{"id":"123","a":1}""")

val id = (json \ "id").as[JsString] // find the id
val fixed = json.as[JsObject] - ("id") + ("_id", id) // replace

For the more complicated requirements:

Json.parse(str) match
  case JsObject(j) =>
    val newId = JsObject(Seq("$old" -> j("id")))
    val newSs = j("subscriptions") match {
      case JsArray(ss) => JsArray(ss.map {
        case JsObject(s) =>
          val i = s("accountId").as[JsString]
          val n = JsObject(Seq("$old" -> i))
          JsObject(s + ("accountId" -> n))
      })
    }
    JsObject(j - "id" + ("_id" -> newId) + ("subscriptions" -> newSs))
}