json4s convert inherited class to json - json

I am trying to serialize / deserialize to json with json4s native the following case class :
case class UserId(value: String) extends MappedTo[String] with Guid
case class FootballTeamId(value: String) extends Guid
trait Guid extends Id[String] with MappedTo[String]
object Guid {
import scala.reflect.runtime._
import scala.reflect.runtime.universe._
def next[T <: Guid : TypeTag]: T = {
val tt = typeTag[T]
val constructor: MethodSymbol = tt.tpe.members.filter {
m ⇒
m.isMethod && m.asMethod.isConstructor
}.head.asMethod
currentMirror.reflectClass(tt.tpe.typeSymbol.asClass).reflectConstructor(constructor)(UUID.randomUUID().toString).asInstanceOf[T]
}
}
I can do like this :
case object UserIdSerializer extends CustomSerializer[UserId](format => ({
case JString(s) => {
UserId(s)
}
}, {
case s: UserId => JString(s.value)
}))
and have one custom serializer by type, but I'd rather be dry and have one custom serialiser that would do the job for all object of type Guid. I had no luck. I would be grateful for any help I could get on the topic !
Thanks

Related

Circe Encoding Sealed Traits and Co Product Case class Fails

I have the following case class:
case class SmartMeterData(
dateInterval: SmartMeterDataInterval = HalfHourInterval(),
powerUnit: PowerUnit = KWH,
smartMeterId: String,
timestamp: String,
value: Double
) {
def timeIntervalInDuration: FiniteDuration = dateInterval match {
case HalfHourInterval(_) => FiniteDuration(30, TimeUnit.MINUTES)
case FullHourInterval(_) => FiniteDuration(60, TimeUnit.MINUTES)
}
}
object SmartMeterData {
sealed trait SmartMeterDataInterval { def interval: String }
case class HalfHourInterval(interval: String = "HH") extends SmartMeterDataInterval
case class FullHourInterval(interval: String = "FH") extends SmartMeterDataInterval
sealed trait PowerUnit
case object WH extends PowerUnit
case object KWH extends PowerUnit
case object MWH extends PowerUnit
}
I just wrote a very simple unit test to see if Circe works for my scenario:
"SmartMeterData" should "successfully parse from a valid JSON AST" in {
val js: String = """
{
"dateInterval" : "HH",
"powerUnit" : "KWH",
"smartMeterId" : "LCID-001-X-54",
"timestamp" : "2012-10-12 00:30:00.0000000",
"value" : 23.0
}
"""
val expectedSmartMeterData = SmartMeterData(smartMeterId = "LCID-001-X-54", timestamp = "2012-10-12 00:30:00.0000000", value = 23.0)
decode[SmartMeterData](js) match {
case Left(err) => fail(s"Error when parsing valid JSON ${err.toString}")
case Right(actualSmartMeterData) =>
assert(actualSmartMeterData equals expectedSmartMeterData)
}
}
But it fails with the following error:
Error when parsing valid JSON DecodingFailure(CNil, List(DownField(dateInterval)))
Is there a known limitation with circe where it does not yet work for my case above?
There's no reason that Circe wouldn't work, but the way you've designed the data model, there's basically zero chance of any Scala JSON library that can automatically generate an encoder/decoder working without a lot of manual work.
For example
sealed trait SmartMeterDataInterval { def interval: String }
case class HalfHourInterval(interval: String = "HH") extends SmartMeterDataInterval
case class FullHourInterval(interval: String = "FH") extends SmartMeterDataInterval
the schema for both, in any Scala JSON library which automatically derives instances for case classes, would be along the lines of
{ "interval": "HH" }
for HalfHourInterval and
{ "interval": "FH" }
for FullHourInterval (i.e. because each has a single string field named interval, they're effectively the same class). In fact your model allows you to have FullHourInterval("HH"), which for at least one method of generating a decoder for an ADT hierarchy in circe (the one in the documentation which uses shapeless) would be the result of decoding { "interval": "HH" }, since that essentially takes the first constructor in lexical order which matches (i.e. FullHourInterval). If the intent is to only allow full- or half-hour intervals, then I'd suggest expressing that as:
case object HalfHourInterval extends SmartMeterDataInterval { def interval: String = "HH" }
case object FullHourInterval extends SmartMeterDataInterval { def interval: String = "FH" }
I'm not directly familiar with how circe encodes case objects, but you can pretty easily define an encoder and decoder for SmartMeterDataInterval:
object SmartMeterDataInterval {
implicit val encoder: Encoder[SmartMeterDataInterval] =
Encoder.encodeString.contramap[SmartMeterDataInterval](_.interval)
implicit val decoder: Decoder[SmartMeterDataInterval] =
Decoder.decodeString.emap {
case "HH" => Right(HalfHourInterval)
case "FH" => Right(FullHourInterval)
case _ => Left("not a valid SmartMeterDataInterval")
}
}
You would then do something similar to define an Encoder/Decoder for PowerUnit

Serialize Sealed class within a data class using Gson in kotlin

I have created a sealed class for the json field Value under CustomAttribute data class. This field can return String or Array of Strings.
How can we deserialize this sealed class from json?
data class CustomAttribute (
val attributeCode: String,
val value: Value
)
sealed class Value {
class StringArrayValue(val value: List<String>) : Value()
class StringValue(val value: String) : Value()
}
One solution is to use a RuntimeTypeAdapterFactory as per the instructions in this answer
val valueTypeAdapter = RuntimeTypeAdapter.of(Value::class.java)
.registerSubtype(StringArrayValue::class.java)
.registerSubtype(StringValue::class.java)
val gson = GsonBuilder().registerTypeAdapter(valueTypeAdapter).create()
RuntimeTypeAdapter is included in the source code for Gson but not exposed as a Maven artifact.
It is designed to be copy/pasted into your project from here
I created a TypeAdapterFactory implementation specifically to support sealed classes and their subtypes. This works similarly to the RuntimeTypeAdapterFactory (and I used it as a guide to write my class), but will specifically only support sealed types, and will deserialize using object instances of objects with a sealed class supertype (RuntimeTypeAdapterFactory will create a new instance of object types, which breaks equality checks when a single instance is the expectation).
private class SealedTypeAdapterFactory<T : Any> private constructor(
private val baseType: KClass<T>,
private val typeFieldName: String
) : TypeAdapterFactory {
private val subclasses = baseType.sealedSubclasses
private val nameToSubclass = subclasses.associateBy { it.simpleName!! }
init {
if (!baseType.isSealed) throw IllegalArgumentException("$baseType is not a sealed class")
}
override fun <R : Any> create(gson: Gson, type: TypeToken<R>?): TypeAdapter<R>? {
if (type == null || subclasses.isEmpty() || subclasses.none { type.rawType.isAssignableFrom(it.java) }) return null
val elementTypeAdapter = gson.getAdapter(JsonElement::class.java)
val subclassToDelegate: Map<KClass<*>, TypeAdapter<*>> = subclasses.associateWith {
gson.getDelegateAdapter(this, TypeToken.get(it.java))
}
return object : TypeAdapter<R>() {
override fun write(writer: JsonWriter, value: R) {
val srcType = value::class
val label = srcType.simpleName!!
#Suppress("UNCHECKED_CAST") val delegate = subclassToDelegate[srcType] as TypeAdapter<R>
val jsonObject = delegate.toJsonTree(value).asJsonObject
if (jsonObject.has(typeFieldName)) {
throw JsonParseException("cannot serialize $label because it already defines a field named $typeFieldName")
}
val clone = JsonObject()
clone.add(typeFieldName, JsonPrimitive(label))
jsonObject.entrySet().forEach {
clone.add(it.key, it.value)
}
elementTypeAdapter.write(writer, clone)
}
override fun read(reader: JsonReader): R {
val element = elementTypeAdapter.read(reader)
val labelElement = element.asJsonObject.remove(typeFieldName) ?: throw JsonParseException(
"cannot deserialize $baseType because it does not define a field named $typeFieldName"
)
val name = labelElement.asString
val subclass = nameToSubclass[name] ?: throw JsonParseException("cannot find $name subclass of $baseType")
#Suppress("UNCHECKED_CAST")
return (subclass.objectInstance as? R) ?: (subclassToDelegate[subclass]!!.fromJsonTree(element) as R)
}
}
}
companion object {
fun <T : Any> of(clz: KClass<T>) = SealedTypeAdapterFactory(clz, "type")
}
}
Usage:
GsonBuilder().registerTypeAdapter(SealedTypeAdapterFactory.of(Value::class)).create()
I have successfully serialized and de-serialized a sealed class in the past, with a disclaimer of using Jackson, not Gson as my serialization engine.
My sealed class has been defined as:
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, visible = true)
sealed class FlexibleResponseModel
class SnapshotResponse(val collection: List<EntityModel>): FlexibleResponseModel()
class DifferentialResponse(val collection: List<EntityModel>): FlexibleResponseModel()
class EventDrivenResponse(val collection: List<EntityEventModel>): FlexibleResponseModel()
class ErrorResponse(val error: String): FlexibleResponseModel()
With the annotations used, it required no further configuration for the Jackson instance to properly serialize and de-serialize instances of this sealed class granted that both sides of the communication possessed a uniform definition of the sealed class.
While I recognise that JsonTypeInfo is a Jackson-specific annotation, perhaps you might consider switching over from Gson if this feature is a must - or you might be able to find an equivalent configuration for Gson which would also include the class identifier in your serialized data.

Json deserialization of Scala case objects with spray-json

I am trying to write a custom JsonReader using spray-json for the following domain model:
sealed trait OrderType
object OrderType {
case object MARKET extends OrderType
case object LIMIT extends OrderType
case object STOP extends OrderType
case object MARKET_IF_TOUCHED extends OrderType
case object TAKE_PROFIT extends OrderType
case object STOP_LOSS extends OrderType
case object TRAILING_STOP_LOSS extends OrderType
}
Here is the custom JsonReader I created for this purpose:
implicit object OrderTypeJsonReader extends JsonReader[OrderType] {
def read(value: JsValue): OrderType = value match {
case JsString("MARKET") => MARKET
case JsString("LIMIT") => LIMIT
case JsString("STOP") => STOP
case JsString("MARKET_IF_TOUCHED") => MARKET_IF_TOUCHED
case JsString("TAKE_PROFIT") => TAKE_PROFIT
case JsString("STOP_LOSS") => STOP_LOSS
case JsString("TRAILING_STOP_LOSS") => TRAILING_STOP_LOSS
case _ => deserializationError("OrderType expected")
}
}
Given that the json string and the name of the case object are the same, is there any way to avoid code duplication here?
You could try to replace pattern match with a partial function(or Map):
val orderTypes = List(MARKET, LIMIT, STOP, MARKET_IF_TOUCHED, TAKE_PROFIT, STOP_LOSS, TRAILING_STOP_LOSS)
val string2orderType: Map[JsValue, OrderType] =
orderTypes.map(ot => (JsString(ot.toString), ot)).toMap
implicit object OrderTypeJsonReader extends JsonReader[OrderType] {
def read(value: JsValue): OrderType =
string2orderType.getOrElse(value, deserializationError("OrderType expected"))
}
The disadvantage is that you have to specify the list of all case objects manually. You can try to use reflection to generate it.
Maybe this question would be helpful for that Getting subclasses of a sealed trait . Then you can have:
import scala.reflect.runtime.universe
private val tpe = universe.typeOf[OrderType]
private val clazz = tpe.typeSymbol.asClass
private def objectBy[T](name: String): T = Class.forName(OrderType.getClass.getName + name + "$").newInstance().asInstanceOf[T]
val string2orderType: Map[JsValue, OrderType] = clazz.knownDirectSubclasses.map { sc =>
val objectName = sc.toString.stripPrefix("object ")
(JsString(objectName), objectBy[OrderType](objectName))
}.toMap
implicit object OrderTypeJsonReader extends JsonReader[OrderType] {
def read(value: JsValue): OrderType = string2orderType.getOrElse(value, deserializationError("OrderType expected"))
}
Please, also see this discussion about adding a default case class format to Spray: https://github.com/spray/spray-json/issues/186
UPDATE to address the comments
Is it possible to 'generify' it for any type T? I have quite a few of those sealed trait / case object enumerations and would prefer to have the boilerplate kept to minimum.
I've come up with this:
import spray.json._
import Utils._
sealed trait OrderStatus
object OrderStatus {
case object Cancelled extends OrderStatus
case object Delivered extends OrderStatus
// More objects...
implicit object OrderStatusJsonReader extends ObjectJsonReader[OrderStatus]
}
sealed trait OrderType
object OrderType {
case object MARKET extends OrderType
case object LIMIT extends OrderType
// More objects...
implicit object OrderTypeJsonReader extends ObjectJsonReader[OrderType]
}
object Utils {
import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
def objectBy[T: ClassTag](name: String): T = {
val c = implicitly[ClassTag[T]]
Class.forName(c + "$" + name + "$").newInstance().asInstanceOf[T]
}
def string2trait[T: TypeTag : ClassTag]: Map[JsValue, T] = {
val clazz = typeOf[T].typeSymbol.asClass
clazz.knownDirectSubclasses.map { sc =>
val objectName = sc.toString.stripPrefix("object ")
(JsString(objectName), objectBy[T](objectName))
}.toMap
}
class ObjectJsonReader[T: TypeTag : ClassTag] extends JsonReader[T] {
val string2T: Map[JsValue, T] = string2trait[T]
def defaultValue: T = deserializationError(s"${ implicitly[ClassTag[T]].runtimeClass.getCanonicalName } expected")
override def read(json: JsValue): T = string2T.getOrElse(json, defaultValue)
}
}
Then you can use it like:
import OrderType._
import OrderStatus._
JsString("MARKET").convertTo[OrderType]
JsString(OrderStatus.Cancelled.toString).convertTo[OrderStatus]
I also tried code from spray-json github issue and it can be used like so:
implicit val orderTypeJsonFormat: RootJsonFormat[OrderType] =
caseObjectJsonFormat(MARKET, LIMIT, STOP, MARKET_IF_TOUCHED, TAKE_PROFIT, STOP_LOSS, TRAILING_STOP_LOSS)
Unfortunately, this requires you to specify all of the objects explicitly. If you want it like so, then, I think, my first suggestion (without reflection) is better. (Because it is without reflection :-) )

json4s and scala: creating custom serializer without specifying all fields

I have two case classes and a trait in the following formats:
trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent
And another case class which contains a list of Parents:
case class ParentResponse(total: Int, results: List[Parent])
Basically the json response might have a list of objects which can either be of type ChildClassOne or ChildClassTwo.
Because of this (I think) I need to create a custom serializer:
class ParentSerializer extends CustomSerializer[Parent](format => ( {
case JObject(List(JField("kind", JString(kind)), JField("id", JString(id))))
if kind == "first_type" => ChildClassOne(kind, id)
case JObject(List(JField("kind", JString(kind)), JField("id", JString(id))))
if kind == "second_type" => ChildClassTwo(kind, id)
}, {
case _ => null
}))
This works fine. Problem is that these objects might get quite big and I don't want to specify every single field in custom serializer. I'm also not modifying the properties in any way, and am using the custom serializer just to return the right type of case class based on the kind field.
Is there any way to avoid specifying every single field in JObject and just have the non-custom serializer take care of creating the right case class? eg.
case JObject(List(JField("kind", JString(kind))))
if kind == "first_type" => read[ChildClassOne](format)
You don't need a custom serializer but you need some custom TypeHints to specify the mapping between your custom "kind" field and the class of the object.
trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent
case class ParentResponse(total: Int, results: List[Parent])
object MyTypeHints extends TypeHints {
// map class to kind and viceversa
val classToHint: Map[Class[_], String] = Map (
classOf[ChildClassOne] -> "first_type",
classOf[ChildClassTwo] -> "second_type"
)
val hintToClass = classToHint.map(_.swap)
override val hints: List[Class[_]] = List(classOf[ChildClassOne], classOf[ChildClassTwo])
override def classFor(hint: String): Option[Class[_]] = hintToClass.get(hint)
override def hintFor(clazz: Class[_]): String = classToHint(clazz)
}
implicit val formats = Serialization.formats(MyTypeHints).withTypeHintFieldName("kind")
val obj = ParentResponse(2, List(ChildClassOne(id = "one"), ChildClassTwo(id = "two")))
val serialized = Serialization.write(obj)
val deserializedFromString = Serialization.read[ParentResponse](
"""{"total":2,"results":[{"kind":"first_type","kind":"first_type","id":"one"},
{"kind":"second_type","kind":"second_type","id":"two"}]}""")
val deserializedFromSerialized = Serialization.read[ParentResponse](serialized)
assert(obj == deserializedFromString)
assert(obj == deserializedFromSerialized)
If you don't need to customize the type hint field, you can use the default ones. Search for Serializing polymorphic Lists in the readme
I managed to solve the problem using a Serializer in the end:
trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent
case class ParentResponse(total: Int, results: List[Parent])
class ParentSerializer extends Serializer[Parent] {
private val ParentClass = classOf[Parent]
implicit val formats = DefaultFormats
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
case (TypeInfo(ParentClass, _), json) => json match {
case JObject(JField("kind", JString(kind)) :: _) => kind match {
case "first_type" => json.extract[ChildClassOne]
case "second_type" => json.extract[ChildClassTwo]
}
case _ => throw new MappingException("Invalid kind")
}
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Map()
}
implicit val formats = DefaultFormats + new ParentSerializer

Vector deserialization by using lift-json

How can i deserialize json array using lift-json to scala vector?
For example:
case class Foo(bar: Vector[Bar])
trait Bar {
def value: Int
}
case class Bar1(value: Int) extends Bar
case class Bar2(value: Int) extends Bar
import net.liftweb.json.{ShortTypeHints, Serialization, DefaultFormats}
implicit val formats = new DefaultFormats {
override val typeHintFieldName = "type"
override val typeHints = ShortTypeHints(List(classOf[Foo],classOf[Bar1],classOf[Bar2]))
}
println(Serialization.writePretty(Foo(Vector(Bar1(1), Bar2(5), Bar1(1)))))
The result is:
{
"type":"Foo",
"bar":[{
"type":"Bar1",
"value":1
},{
"type":"Bar2",
"value":5
},{
"type":"Bar1",
"value":1
}]
}
Good. But when i try to deserialize this string
println(Serialization.read[Foo](Serialization.writePretty(Foo(Vector(Bar1(1), Bar2(5), Bar1(1))))))
i get an exception:
net.liftweb.json.MappingException: Parsed JSON values do not match
with class constructor args=List(Bar1(1), Bar2(5), Bar1(1)) arg
types=scala.collection.immutable.$colon$colon constructor=public
test.Foo(scala.collection.immutable.Vector)
It's means that json array associated with scala list, not vector type that defined in class Foo. I know that there is way to create custom serializer by extending net.liftweb.json.Serializer and include it to formats value. But how can i restore type of objects that stores in Vector. I wanna get result of deserializing like this:
Foo(Vector(Bar1(1), Bar2(5), Bar1(1)))
I've often been annoyed by the List-centricness of Lift, and have found myself needing to do similar things in the past. The following is the approach I've used, adapted a bit for your example:
trait Bar { def value: Int }
case class Bar1(value: Int) extends Bar
case class Bar2(value: Int) extends Bar
case class Foo(bar: Vector[Bar])
import net.liftweb.json._
implicit val formats = new DefaultFormats { outer =>
override val typeHintFieldName = "type"
override val typeHints =
ShortTypeHints(classOf[Bar1] :: classOf[Bar2] :: Nil) +
new ShortTypeHints(classOf[Foo] :: Nil) {
val FooName = this.hintFor(classOf[Foo])
override def deserialize = {
case (FooName, foo) => foo \ "bar" match {
case JArray(bars) => Foo(
bars.map(_.extract[Bar](outer, manifest[Bar]))(collection.breakOut)
)
case _ => throw new RuntimeException("Not really a Foo.")
}
}
}
}
Kind of ugly, and could probably be cleaned up a bit, but it works.
You could add an implicit conversion:
implicit def listToVect(list:List[Bar]):Vector[Bar] = list.map(identity)(breakOut)
after that, Serialization.read[Foo] works as expected.