Reputation: 666
I'm coming from a Java background where mapping POJOs is really, really simple. I expect that the same is true for scala/play (and it is for case classes) but things really seem to soil the sheets when I try to add a bit of hierarchy.
So, my specific problem is that I'm trying to send various messages over a websocket and JSON is just really good for this. But, I don't want to repeat common attributes in every class for every type of message, hence the inheritance.
So, I'm trying something like this:
class WSMessage(messageType:String)
class EventMessage(eventType:String) extends WSMessage("event")
class AlertMessage(alertType:String) extends WSMessage("alert")
and there may be further specificity....
class LoginEventMessage(login:Login) extends EventMessage("login")
class OrderEventMessage(order:Order) extends EventMessage("order")
class TerrainAlertMessage(terrain:Terrain) extends AlertMessage("terrain")
Now, assuming I've put something like:
object LoginEventMessage { implicit val fmt = Json.format[LoginEventMessage] }
AND
object Login { implicit val fmt = Json.format[Login] }
on every object at every step of the hierarchy (which is really annoying) I still only get the lowest level attributes converted to JSON.
For example
Json.toJson(new LoginEventMessage(theLogin))
yields something like
{ "login": { "username": "foo", "timestamp": "0000000" } }
rather than
{ "messageType": "event", "eventType": "login", "login": { "username": "foo", "timestamp": "0000000" } }
Any guidance on how to accomplish this without repeating all attributes at the lowest level would be welcome!
Cheers.
Edited To Add Attempt #2 I tried modifying things a bit based on Zoltán's comment. Here is the result:
object Login { implicit val fmt = Json.format[Login] }
case class Login(username:String, timestamp:DateTime)
object WSMessage { implicit val fmt = Json.format[WSMessage] }
class WSMessage(val messageType:String)
object EventMessage { implicit val fmt = Json.format[EventMessage] }
class EventMessage(val eventType:String) extends WSMessage("event")
object LoginEventMessage { implicit val fmt = Json.format[LoginEventMessage] }
class LoginEventMessage(val login:Login) extends EventMessage("login")
We get compile errors because these aren't case classes (they don't like inheritance).
[error] /app/model/dto/WSMessage.scala:15: No unapply or unapplySeq function found
[error] object WSMessage { implicit val fmt = Json.format[WSMessage] }
[error] ^
[error] /app/model/dto/WSMessage.scala:17: No unapply or unapplySeq function found
[error] object EventMessage { implicit val fmt = Json.format[EventMessage] }
[error] ^
[error] /app/model/dto/WSMessage.scala:19: No unapply or unapplySeq function found
[error] object LoginEventMessage { implicit val fmt = Json.format[LoginEventMessage] }
[error] ^
[error] three errors found
[error] (compile:compileIncremental) Compilation failed
[error] application -
Upvotes: 3
Views: 439
Reputation: 22166
Remember that when declaring classes, as opposed to case classes, all the parameters of the constructor are protected
fields, unless declared with val
.
So in order to expose all the fields to the JSON formatter, you need to declare them with val
:
class WSMessage(val messageType:String)
class EventMessage(val eventType:String) extends WSMessage("event")
class AlertMessage(val alertType:String) extends WSMessage("alert")
class LoginEventMessage(val login:Login) extends EventMessage("login")
class OrderEventMessage(val order:Order) extends EventMessage("order")
class TerrainAlertMessage(val terrain:Terrain) extends AlertMessage("terrain")
Upvotes: 1