Play Framework - add a field to JSON object
Solution 1
JsObject
has a +
method that allows you to add fields to an object, but unfortunately your jsonObject
is statically typed as a JsValue
, not a JsObject
. You can get around this in a couple of ways. The first is to use as
:
scala> jsonObject.as[JsObject] + ("c" -> Json.toJson(3))
res0: play.api.libs.json.JsObject = {"a":1,"b":2,"c":3}
With as
you're essentially downcasting—you're telling the compiler, "you only know that this is a JsValue
, but believe me, it's also a JsObject
". This is safe in this case, but it's not a good idea. A more principled approach is to use the OWrites
directly:
scala> val jsonObject = classAWrites.writes(classAObject)
jsonObject: play.api.libs.json.JsObject = {"a":1,"b":2}
scala> jsonObject + ("c" -> Json.toJson(3))
res1: play.api.libs.json.JsObject = {"a":1,"b":2,"c":3}
Maybe someday the Json
object will have a toJsonObject
method that will require a OWrites
instance and this overly explicit approach won't be necessary.
Solution 2
I found a solution myself. In fact the JsValue, which is the return type of Json.toJson has no such method, but the JsObject (http://www.playframework.com/documentation/2.2.x/api/scala/index.html#play.api.libs.json.JsObject) does, so the solution is:
val jsonObject = Json.toJson(classAObject).as[JsObject]
jsonObject + ("c", JsNumber(3))
I hope someone will find this useful :)
Paweł Kozikowski
Updated on May 23, 2020Comments
-
Paweł Kozikowski almost 4 years
I have a problem with adding a field to Json object in Play Framework using Scala:
I have a case class containing data. For example:
case class ClassA(a:Int,b:Int)
and I am able to create a Json object using Json Writes:
val classAObject = ClassA(1,2) implicit val classAWrites= Json.writes[ClassA] val jsonObject = Json.toJson(classAObject)
and the Json would look like:
{ a:1, b:2 }
Let's suppose I would like to add an additional 'c' field to the Json object. Result:
{ a:1, b:2, c:3 }
How do I do that without creating a new case class or creating my Json object myself using Json.obj? I am looking for something like:
jsonObject.merge({c:3})
Any help appreciated!
-
Tvaroh over 9 yearsPer signature,
Writes.writes
returnsJsValue
, so I don't understand how you can get rid of upcasting (not in the REPL). -
elmalto over 9 yearsTvaroh is right, it return JsValue as far as I can see
-
Travis Brown over 9 years@elmalto Try it out—
classAWrites
will be statically typed asOWrites[ClassA]
(in both Play 2.2 and 2.3 and on 2.10 and 2.11). This is due to "underspecified but intended" behavior of Scala's macros (see my question here for details). -
elmalto over 9 yearsSkipping the definition for the class and writer for brevity, but this is what the repl gives me:
scala> SessionCreatorWriter.writes(s)
res2: play.api.libs.json.JsValue
-
Travis Brown over 9 years@elmalto If you define a
Writes
instance you'll getJsValue
, but if you define anOWrites
instance—either manually or using theJson.writes
macro, which is what the OP is doing—you'll get aJsObject
. -
elmalto over 9 years@TravisBrown maybe I should've posted it then:
implicit val SessionCreatorWriter = Json.writes[SessionCreator]
val s = SessionCreator("User1",None,None,None,1,None,None)
SessionCreatorWriter.writes(s)
and I get a JsValue -
Wrench almost 9 yearsThere's actually no need for the paranthesis. This works:
jsobj + "key" -> entity.key
-
Travis Brown almost 9 years@Wrench That does something completely different.
+
and-
have the same precedence, so the parentheses are definitely necessary. -
Basil over 3 yearsWhy is
Json.toJson(3)
needed in this scenario. Even though it's not needed when doingJson.obj("a" -> 3)
??