I have two JsValue created from case class, i.e. Book and Book detail
val bookJson = Json.tojson(Book)
val bookDetailJson = Json.tojson(BookDetail)
Play has a lot of new features for JSON right now. This would be a nice showcase for the Format[A]
trait (see Scala Json Inception) which you could include implicitly as I will show, or explicitly to the methods that require an implicit Format[A]/Reads[A]/Writes[A]
.
Create a case class to represent your JSON objects,
case class Book(id: Int, name: String)
case class BookDetail(id: Int, author: String, publicationDate: Int, pages: Int)
Create companion objects that contain the implicit Format[A]
so that Format/Reads/Writes
will automatically be in scope when you need them.
object Book {
implicit val fmt: Format[Book] = Json.format[Book]
}
object BookDetail {
implicit val fmt: Format[BookDetail] = Json.format[BookDetail]
}
Now you could do something like this,
val bookJson = Json.toJson(Book(1, "A Brief History Of Time"))
val bookDetailJson = Json.toJson(BookDetail(1, "Steven Hawking", 1988, 256))
bookJson.as[JsObject].deepMerge(bookDetailJson.as[JsObject])
And you will have an object like this,
{
id: 1,
name: "A Brief History Of Time",
author: "Steven Hawking",
publicationDate: 1988,
pages: 256
}
I've tried this in the REPL but it does not work, in a Play application it does just fine though. Also in a production scenario we would likely use asOpt[T]
in place of as[T]
.
Here is an example of why asOpt[T]
may be better suited, suppose instead of a valid JSON object for book you get,
val bookJson = Json.toJson("not a book")
You will end up with a
[JsResultException: JsResultException(errors:List((,List(ValidationError(validate.error.expected.jsobject,WrappedArray())))))]
But suppose instead you change your method to use asOpt[T]
,
bookJson.asOpt[JsObject].getOrElse(Json.obj()).deepMerge(bookDetailJson.asOpt[JsObject].getOrElse(Json.obj()))
Now you will end up with at least a partial JSON object,
{
id: 1,
author: "Steven Hawking",
publicationDate: 1988,
pages: 256
}
So depending on how you would like to handle improperly formatted JSON you could choose either option.
JsObject is subtype of JsValue.
JsValue can be simple converted to the JsObject using as or asOpt methods from JsValue. Example:
val someJsValue = ....
val asObject:JsObject = someJsValue.as[JsObject]
val asObjectMaybe:Option[JsObject] = v.asOpt[JsObject]
In the case of JsArray you can not use above code. If you use play and parse JSON with array, then Json.toJson(...) produces JsValue which is JsArray actually. You need to convert JsArray as following:
val someJsValueButArray = ....
val asJsArray:JsArray = Json.toJson(someJsValueButArray).as[JsArray]
val asSeqOfJsObjects:Seq[JsObject] = asJsArray.value.map(_.as[JsObject])