Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json Serialization for Trait with Multiple Case Classes (Sum Types) in Scala's Play

I often have to serialize/deserialize sum types (like Either[S,T]), and I haven't yet found a general or elegant way to do it. Here's an example type (essentially equivalent to Either)

sealed trait OutcomeType
case class NumericOutcome(units: String)              extends OutcomeType
case class QualitativeOutcome(outcomes: List[String]) extends OutcomeType

Here's my best effort at a companion object that implements serialization. It works, but it's very tiresome to write these sorts of things over and over for every sum type. Are there any suggestions for making it nicer and/or more general?

import play.api.libs.json._
import play.api.libs.functional.syntax._

object OutcomeType {

  val fmtNumeric     = Json.format[NumericOutcome]
  val fmtQualitative = Json.format[QualitativeOutcome]

  implicit object FormatOutcomeType extends Format[OutcomeType] {
    def writes(o: OutcomeType) = o match {
      case n@NumericOutcome(_)     => Json.obj("NumericOutcome"     -> Json.toJson(n)(fmtNumeric))
      case q@QualitativeOutcome(_) => Json.obj("QualitativeOutcome" -> Json.toJson(q)(fmtQualitative))
    }

    def reads(json: JsValue) = (
      Json.fromJson(json \ "NumericOutcome")(fmtNumeric) orElse
      Json.fromJson(json \ "QualitativeOutcome")(fmtQualitative)
    )
  }
}
like image 879
davidsd Avatar asked Aug 26 '13 23:08

davidsd


1 Answers

I think that is about as simple as you can make it, if you want to avoid writing the code for each explicit subtype maybe you could do it with reflection, use jackson directly or some other json library with reflection support. Or write your own macro to generate the Format from a list of subtypes.

like image 104
johanandren Avatar answered Oct 13 '22 01:10

johanandren