Search code examples
jsonscalaplayframeworkplayframework-json

JsPath.json.update doesn't work with array element in the path (IdxPathNode)?


Are my eyes deceiving me or can I not update a nested node with a JsPath containing an array element in between? (e.g. /a/b(0)/c)

val pnJson = Json.parse("""{"a": {"b": [ {"c": { "d": 1 } } ] } } """)                                     
val pnJsPath = (__ \ "a" \ "b")(0) \ "c"                                                                   
val pnTrans = pnJsPath.json.update ( __.read[JsObject].map{ _ ++ Json.obj( "e" -> 2 )} )

pnJson.transform(pnTrans)

//result: java.lang.RuntimeException: expected KeyPathNode

If I cant use __.json.update, How can I accomplish this? Trying to accomplish this with immutablity.


Solution

  • The short answer is you can't do that with JsPath. That ticket mentions using the JsZipper library as a better way to manipulate JSON. If you choose to use it, you can do:

    scala> import play.api.libs.json.extensions._
    import play.api.libs.json.extensions._
    
    scala> import play.api.libs.json.monad.syntax._
    import play.api.libs.json.monad.syntax._
    
    scala> val pnJson = Json.parse("""{"a": {"b": [ {"c": { "d": 1 } } ] } } """) 
    pnJson: play.api.libs.json.JsValue = {"a":{"b":[{"c":{"d":1}}]}}
    
    scala> val pnJsPath = (__ \ "a" \ "b")(0) \ "c" 
    pnJsPath: play.api.libs.json.JsPath = /a/b(0)/c
    
    scala> pnJson.update(pnJsPath, _.set(__ \ "e" -> JsNumber(2)))
    res0: play.api.libs.json.JsValue = {"a":{"b":[{"c":{"d":1,"e":2}}]}}
    

    Otherwise, your best bet is to break your task into two parts, the first part being parsing the array and the second part being transforming it into a new array with modified contents.