How to create generic implicit conversions for json marshalling - scala

I want to limit the construction of a case class to certain types and then be able to marshall that data back and forth.
For example, let's say I have a "home" case class that takes in a "kind" argument. I want to restrict the "kind" argument to a list of approved housing types, e.g., Apartment, Condo, etc.
object Home {
// this will create an implicit conversion to json object
implicit lazy val jsFormat = Jsonx.formatCaseClass[Home]
}
case class Home(owner: String, kind: HousingType)
What I need now is a way to marshall the various child types of HousingType. For example, here are some relationships:
trait HousingType
case object Apartment extends HousingType
case object Condo extends HousingType
Predictably, attempting to use this without specifying an implicit conversion yields the following error:
"could not find implicit value for parameter helper: ... HousingType"
Is there a way to create a generic implicit conversion for this?

You have to specify how your JSON marshaller has to transform your case object, as you have case class, it's quite simple for JSON marshaller to follow default behavior - take JSON field names from a case class and their type.
You need to indicate how to marshall/unmarshall case object directly, for instance via an implicit conversion.
implicit object HousingTypeMarshaller extends Writes[HousingType] {
def writes(housingType: HousingType) = housingType match {
case Apartment => Json.toJson("Apartment")
case Condo => Json.toJson("Condo")
}
}
p.s. I use usual play.json in this example because I didn't find any reason to use Jsonx, suggest you faced a limitation on 22 fields with Play Json, usual Play Json is suitable for this situation with case object.

Related

Json.reads on abstract class (Sealed trait is not supported: no known subclasses)

I have an abstract class and object
sealed abstract class Granularity() {
// some values and methods
}
object Granularity {
final private case class WeekGranularity(name: String, windowSize: Int) extends Granularity("week", "'7' day") {
// overriding methods
}
val Week: Granularity = WeekGranularity(name = "week", windowSize = 1)
}
I am using it in some other class like this
case class Meta(granularity: Granularity)
object Meta {
implicit val granularityWrites = Writes[Granularity](d => JsString(d.toString))
implicit val metaWrites = Json.writes[Meta]
}
Now when writing a spec like this, I get an error
class ControllerSpec {
"MetaController" should {
"return" {
.
.
.
// play 2.13 resp
resp.body[JsValue].asOpt[Meta] should beSome(expectedMeta)
// ERROR: No Json deserializer found for type models.Meta. Try to implement an implicit Reads or Format for this type
}
}
}
When I add an implicit Read on top inside Spec class, I still get an error
implicit val granularityReads = Json.reads[Granularity] // ERROR: Sealed trait Granularity is not supported: no known subclasses
implicit val metaReads = Json.reads[Meta]
I can do this to compare json, which works and I don't have to create any implicit.
resp.body[JsValue] shouldEqual Json.toJson(metricSignTimeseries)
But I want to understand how can I implement an implicit Read on Granularity?
In order to read a Granularity from JSON data the library must be able to create an instance of Granularity in order to return it. But Granularity is an abstract class and you can't create instances of an abstract class.
Json.reads needs to be parameterised with a concrete class that can be instanciated by the library, or you need to write a custom Reads[Granularity] that creates and returns an appropriate subclass of Granularity.
I suggest that you don't put functionality in the classes that you use to read/write JSON or use complex class hierarchies. Just read the data into simple case class instances that directly match the JSON format, and then process them into application classes. This allows the storage formats and the internal application data formats to change independently rather than being tightly linked.

Spray-Json serialize interface type

using spray-json how can I serialize following class
case class Vegetable(name: String, color: String, seller:ISeller)
here ISeller is a Java Interface. I am new to spray-json an not sure how this can be serialized and deserialized.
I tried this but it gives runtime error
implicit val VegetableFormat = jsonFormat3(Vegetable)
Any pointer here will be great.
You need to define a way to convert to/from JSON for your ISeller object.
In addition to code you supplied you need to define formatter for ISeller like the following:
implicit object ISellerJsonFormat extends RootJsonFormat[ISeller] {
def write(c: ISeller) = JsNull
def read(value: JsValue) = null
}
The snippet above just ignores ISeller so vegetable.toJson would produce:
{"name":"Onion","color":"red","seller":null}
If you want to read/write something more meaningful you can implement more complex logic. See the "Providing JsonFormats for other Types" section in https://github.com/spray/spray-json .

How to define implicit Writes in trait

I have multiple case classes representing values in DB for ex User which saves user based properties like name / age / address and CallLog which saves timestamp / status_of_call
What i want to achieve
I want to have a helper function which accepts list of models and checks if the list is empty then returns "error" otherwise should return json array of the list.
My Approach
I want to have a trait which groups certain models in it and the helper method will accept either the trait or List of it in order to check or may be have a generic which implements the trait.
Problem
Since implicit writes are tightly coupled with the model class, compiler throws the error on the line Json.toJson(list)
Things i have tried
Kept implicit in trait and got recursive type error
I am scala noob pardon me if this sounds silly
Thanks in advance
Since User, CallLog, etc. will be serialized differently, Each Writes[T] will be different for each implementation of your Model trait, so a Writes[Model] has to know about the implementation it is trying to serialize.
It is therefore not possible to have it part of the Model trait, because this information isn't known yet when you define it.
A workaround in your case would be to define your Writes[Model] in the scope of your helper function instead.
An implementation of your helper function could be like this :
import play.api.libs.json.{JsValue, Json, Writes}
sealed trait Model
case class User(name: String, age: String, address: String) extends Model
object User {
implicit val userWrites = Json.writes[User]
}
case class CallLog(timestamp: String, status_of_call: String) extends Model
object CallLog {
implicit val callLogWrites = Json.writes[CallLog]
}
implicit val modelWrites = new Writes[Model] {
override def writes(o: Model): JsValue = o match {
case u: User => Json.toJson(u)
case cl: CallLog => Json.toJson(cl)
}
}
def helper(models: Model*): Either[JsValue, String] = models match {
case Nil => Right("Error")
case _ => Left(Json.toJson(models))
}
helper(User("John", "32", "..."))
helper(User("John", "32", "..."), CallLog("now", "In progress"))

Case class without parameters alternative

During some simple scala coding exercise I ran into ideological problem of case classes without parameters and constructor parameters duplication.
It all started with the following two quite simple classes:
trait Namespace
case class Reply[T](namespace: Namespace, correlation: String, data: Try[T])
abstract class Request(val namespace: Namespace, val id: String = UUID.randomUUID().toString) {
def success[T](data: T) = Reply(namespace, id, Try(data))
def failure(msg: String) = Reply(namespace, id, Failure(new RuntimeException(msg)))
}
Now let's assume i have an entity Post and I want to add All class as a command to query all records of type Post. In my current set up it would be easier to actually write the following:
case class All extends Request(Posts)
However in this case I get compiler warning that case classes without parameters are deprecated. So one might suggest to rewrite it into the following:
case object All extends Request(Posts)
However in this case object All will be instantiated only once along with its id field which would like to avoid having unique id for each request.
Could you please suggest a better way of representing All command so that it would not be required to duplicate constructor arguments?
Thanks in advance?
The actual warning is that
case classes without a parameter list are not allowed; use either case
objects or case classes with an explicit `()' as a parameter list.
So give this class an empty parameter list, just as suggested by the compiler:
case class All() extends Requests(Posts)
Don't use a case class or case object, just use a companion apply instead. You don't really want an object here anyway, if you need a unique ID for every request.
class All extends Requests(Posts)
object All {
def apply(): All = new All()
}
getPosts(All())

Using value classes in scala to implement trait methods?

I have a trait that defines a function--I don't want to specify how it will work until later. This trait is mixed in with several case classes, like so:
trait AnItem
trait DataFormatable {
def render():String = "" // dummy implementation
}
case class Person(name:String, age:Int) extends DataFormatable with AnItem
case class Building(numFloors:Int) extends DataFormatable with AnItem
Ok, so now I want includable modules that pimp specific implementations of this render behavior. Trying to use value classes here:
object JSON {
implicit class PersonRender( val p:Person ) extends AnyVal {
def render():String = {
//render json
}
}
// others
}
object XML {
implicit class PersonRender( val p:Person ) extends AnyVal {
def render():String = {
//render xml
}
}
// others
}
The ideal use would look like this (presuming JSON output desired):
import JSON._
val p:AnItem = Person("John",24)
println(p.render())
All cool--but it doesn't work. Is there a way I can make this loadable-implementation thing work? Am I close?
The DataFormatable trait is doing nothing here but holding you back. You should just get rid of it. Since you want to swap out render implementations based on the existence of implicits in scope, Person can't have it's own render method. The compiler will only look for an implicit conversion to PersonRender if Person doesn't have a method named render in the first place. But because Person inherits (or is forced to implement) render from DataFormatable, there is no need to look for the implicit conversion.
Based on your edit, if you have a collection of List[AnItem], it is also not possible to implicitly convert the elements to have render. While each of the sub-classes may have an implicit conversion that gives them render, the compiler doesn't know that when they are all piled into a list of a more abstract type. Particularly an empty trait such as AnItem.
How can you make this work? You have two simple options.
One, if you want to stick with the implicit conversions, you need to remove DataFormatable as the super-type of your case classes, so that they do not have their own render method. Then you can swap out XML._ and JSON._, and the conversions should work. However, you won't be allowed mixed collections.
Two, drop the implicits altogether and have your trait look like this:
trait DataFormatable {
def toXML: String
def toJSON: String
}
This way, you force every class that mixes in DataFormatable to contain serialization information (which is the way it should be, rather than hiding them in implicits). Now, when you have a List[DataFormatable], you can prove all of the elements can both be converted to JSON or XML, so you can convert a mixed list. I think this would be much better overall, as the code should be more straightforward. What imports you have shouldn't really be defining the behavior of what follows. Imagine the confusion that can arise because XML._ has been imported at the top of the file instead of JSON._.