Reputation: 535
I have seen multiple post on this question but I did not find any solution to avoid passing default value. I have following code:
trait Message
case class StringMessag(msg:String) extends Message
case class PersonalMessage(msg:String, from:String, to:String) extends Message
I would like to marshal and unmarshal during httprequest processing for the same. I followed the approach described at multiple forums:
I modified my case classes to add kind type as following:
case class StringMessag(msg:String, val kind:String="String") extends Message
case class PersonalMessage(msg:String, from:String, to:String, val kind:String="Personal") extends Message
implicit val stringMessageFormat = jsonFormat2(StringMessage.apply)
implicit val personalMessageFormat = jsonFormat4(PersonalMessage.apply)
implicit object MyJsonFormat extends RootJsonFormat[Message] {
def write(a: Message) = a match {
case a: StringMessage => a.toJson
case b: PersonalMessage => b.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("kind") match {
case JsString("String") => value.convertTo[StringMessage]
case JsString("Personal") => value.convertTo[PersonalMessage]
}
}
This works fine, but client program has to send additional default value for "kind", even though kind has default value define in case class. Can't we avoid that additional value to passed by the client so RouteDSL can unmarshal without any error?
I have tried the following solution; which works, but would be looking more clean way of doing.
case class StringMessag(msg:String) extends Message
case class PersonalMessage(msg:String, from:String, to:String) extends Message
implicit val stringMessageFormat = jsonFormat1(StringMessage.apply)
implicit val personalMessageFormat = jsonFormat3(PersonalMessage.apply)
def read(value: JsValue) =
value match {
case obj:JsObject if(obj.fields.size==1 && obj.fields.contains("msg")) => value.convertTo[StringMessage]
case obj:JsObject if(obj.fields.size==3 && obj.fields.contains("from")) => value.convertTo[PersonalMessage]
}
Thanks Arun
Upvotes: 2
Views: 639
Reputation: 1019
I would define a custom JsonFormat[Message]
instance with auto generated formats for each message type. On write method, I would pattern match and write json with auto generated formats and on read method, I would try to parse json with auto generated formats one by one and return first successfull result.
Here is an example implementation
implicit object MessageFormat extends JsonFormat[Message] {
val stringMessageFormat = jsonFormat1(StringMessage)
val personalMessageFormat = jsonFormat3(PersonalMessage)
def write(obj: Message): JsValue = obj match {
case x: StringMessage => x.toJson
case x: PersonalMessage => x.toJson
}
def read(json: JsValue): Message =
Try(personalMessageFormat.read(json))
.orElse(Try(stringMessageFormat.read(json)))
.getOrElse(deserializationError(s"Invalid message format: ${json.toString()}"))
}
Upvotes: 2