spray / spray-json

A lightweight, clean and simple JSON implementation in Scala
Apache License 2.0
974 stars 190 forks source link

Deserialization of abstract class #180

Open ariens opened 8 years ago

ariens commented 8 years ago

Looking for some best practices for dealing with a use case that involves serialization and deserialization of an object hierarchy that based off an abstract class. I have the serialization working as expected but currently no good method for determining the appropriate class to use for deserialization. The only approach I can envision is to a) not allow deserialization (throwing the exception as per the below) or determining if the value can be made into an int, then long, then float, then double (but that would likely be terribly inefficient). Can anyone recommend a best practice using spray-json?

abstract class Metric
case class LongMetric(metric: String, timestamp: Long, value: Long) extends Metric
case class IntMetric(metric: String, timestamp: Long, value: Int) extends Metric
case class FloatMetric(metric: String, timestamp: Long, value: Float) extends Metric
case class DoubleMetric(metric: String, timestamp: Long, value: Double) extends Metric

object MetricJsonProtocol extends DefaultJsonProtocol {
  implicit object MetricJsonFormat extends RootJsonFormat[Metric] {

    def write(m: Metric) = m match {
      case longMetric: LongMetric     => longMetric.toJson
      case intMetric: IntMetric       => intMetric.toJson
      case floatMetric: FloatMetric   => floatMetric.toJson
      case doubleMetric: DoubleMetric => doubleMetric.toJson
    }
    def read(value: JsValue) = value match {
      case _ => deserializationError("deserialization of abstract class not supported")
    }
  }
  implicit val longMetricFmt = jsonFormat3(LongMetric)
  implicit val intMetricFmt = jsonFormat3(IntMetric)
  implicit val floatMetricFmt = jsonFormat3(FloatMetric)
  implicit val doubleMetricFmt = jsonFormat3(DoubleMetric)
}
TimurFayruzov commented 8 years ago

In this situation we changed writer to add 'type' field that captures the concrete type and use this information at deserialization time. I would be curious too to see whether there are more elegant ways to solve this problem.