Skip to content

JsPath.json.put writing to an object in a nested array exception: expected KeyPathNode #82

Description

@noisypants

Play JSON Version (2.5.x / etc)

2.5.15

API (Scala / Java / Neither / Both)

Scala (didn't test Java)

Operating System

Window 10

JDK

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Library Dependencies

Expected Behavior

I would expect to use a JsPath.json.update and JsPath.json.put to update a single field within an object within a nested array. (See code below)

Actual Behavior

When attempting to put a new field in a JsObject that's inside an array, a RuntimeException is generated.
The following code throws an exception:

expected KeyPathNode
java.lang.RuntimeException: expected KeyPathNode
	at play.api.libs.json.JsPath$.step$1(JsPath.scala:142)
	at play.api.libs.json.JsPath$.step$1(JsPath.scala:141)
	at play.api.libs.json.JsPath$.play$api$libs$json$JsPath$$buildSubPath$1(JsPath.scala:147)
	at play.api.libs.json.JsPath$$anonfun$createObj$1.apply(JsPath.scala:152)
	at play.api.libs.json.JsPath$$anonfun$createObj$1.apply(JsPath.scala:150)
	at scala.collection.IndexedSeqOptimized$class.foldl(IndexedSeqOptimized.scala:57)
	at scala.collection.IndexedSeqOptimized$class.foldLeft(IndexedSeqOptimized.scala:66)
	at scala.collection.mutable.WrappedArray.foldLeft(WrappedArray.scala:35)
	at play.api.libs.json.JsPath$.createObj(JsPath.scala:150)
	at play.api.libs.json.PathReads$$anonfun$jsPut$1.apply(JsConstraints.scala:63)
	at play.api.libs.json.PathReads$$anonfun$jsPut$1.apply(JsConstraints.scala:63)
	at play.api.libs.json.Reads$$anon$8.reads(Reads.scala:126)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1$$anonfun$apply$11.apply(JsConstraints.scala:72)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1$$anonfun$apply$11.apply(JsConstraints.scala:72)
	at play.api.libs.json.JsResult$class.flatMap(JsResult.scala:99)
	at play.api.libs.json.JsSuccess.flatMap(JsResult.scala:9)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1.apply(JsConstraints.scala:72)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1.apply(JsConstraints.scala:69)
	at play.api.libs.json.Reads$$anon$8.reads(Reads.scala:126)
	at play.api.libs.json.JsValue$class.validate(JsValue.scala:18)
	at play.api.libs.json.JsObject.validate(JsValue.scala:76)
	at play.api.libs.json.JsReadable$class.transform(JsReadable.scala:29)
	at play.api.libs.json.JsObject.transform(JsValue.scala:76)
  private def setValueByPath(data: JsObject, path: JsPath, value: JsValue) : JsObject = {
     data.transform(__.json.update(path.json.put({ value }))) match {
      case s: JsSuccess[JsObject] => s.get
      case e: JsError => throw new IllegalArgumentException("Failed")
    }
  }
  val data = Json.obj(
    "array" -> Json.arr(Json.obj())
  )
  val newData = setValueByPath(data, __ \ "array" \ 0 \ "field", Json.toJson("arrayField"))

Reproducible Test Case

package jspathtest
import org.scalatest._
import play.api.libs.json._

class TestJsPath extends FlatSpec with Matchers {

  "A JsPath" should "allow setting a value in a nested branch in array" in {

    val data = Json.obj(
      "array" -> Json.arr(Json.obj())
    )
    val newData = setValueByPath(data, __ \ "array" \ 0 \ "field", Json.toJson("arrayField"))
    newData should be (Json.obj("array" -> Json.arr(Json.obj("field" -> "arrayField"))))
  }

  it should "allow setting a value in a root field" in {

    val data = Json.obj(
      "array" -> Json.arr(Json.obj())
    )
    val newData = setValueByPath(data, __ \ "field", Json.toJson("field"))

    newData should be (Json.obj("field" -> "field", "array" -> Json.arr(Json.obj())))
  }


  it should "allow setting a value in a nested obj field" in {

    val data = Json.obj(
      "other" -> Json.obj()
    )
    val newData = setValueByPath(data, __ \ "other" \ "field", Json.toJson("otherField"))
    newData should be (Json.obj("other" -> Json.obj("field" -> "otherField")))
  }

  private def setValueByPath(data: JsObject, path: JsPath, value: JsValue) : JsObject = {
    data.transform(__.json.update(path.json.put({ value }))) match {
      case s: JsSuccess[JsObject] => s.get
      case e: JsError => throw new IllegalArgumentException("Failed")
    }
  }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions