How to convert json to object in Play Framework REST API
I am just getting started with scala and playing with a framework and am sticking with this seemingly simple problem.
I have one REST endpoint where I will post some Json Object. This Json object needs to be converted to object.
The entity is declared by type class classes, and the resulting Json can be any type of case class.
My problem is that I cannot convert the Json to the appropriate entity type because (according to the tutorial) I need to write implicit Reads
in validation with each of the defined fields.
for example
implicit val emailReads: Reads[Email] = (
(JsPath \ "from").read[String] and
(JsPath \ "subject").read[String]
)(Email.apply _)
works great for the example class email. But when I have class classes like this:
abstract class Event
case class OneEventType(type : String) extends Event
case class TwoEventType(type : String, attribute : SomeType) extends Event
And the controller method works based on the event:
def events = Action(BodyParsers.parse.json) { request =>
val eventReceived = request.body.validate[Event]
//do something
Ok(Json.obj("status" ->"OK"))
}
How would I check for the event and build the correct Event object like in the Reads method, do I need to specify each of the fields?
source to share
This should work,
implicit val st: Reads[Event] = new Reads[Event] {
def reads(json: JsValue): JsResult[Event] = {
json match {
case JsObject(Seq(("type", JsString(type)), ("attribute", JsString(attribute)))) => JsSuccess(TwoEventType(type, attribute))
case o: JsObject if (o.value.get("type").isDefined) => JsSuccess(OneEventType(o.value.get("type")))
case a: Any => JsError(a.toString())
}
}
}
source to share
I think you can add a custom read process. Suppose the attribute is of type Int:
abstract class Event
case class OneEventType(type : String) extends Event
case class TwoEventType(type : String, attribute : Int) extends Event
implicit val eventReader: Reads[Event] = (
(JsPath \ "type").read[String] and
(JsPath \ "attribute").readNullable[Int]
)((typeOp, attributeOp) => {
if (attribute.isEmpty) OneEventType(typeOp.get())
else TwoEventType(typeOp.get(), attributeOp.get())
})
(can't test it now, so I'm not sure if it works out of the box).
source to share