I would like to write a function that transforms case classes to Json:
import play.api.libs.json._
def myJson(cc: Product): JsValue = {
Json.toJson(cc) // simplified
}
Each case class has an implicit Writes[T], for example:
case class Test(a: Int)
object Test {
implicit val jsonWrites: Writes[Test] = Json.writes[Test]
}
It is possible to write Json.toJson(new Test(1)) individually, but the myJson function above does not compile because it never knows if cc has an implicit Writes defined.
[How can I write the function signature so that it takes only classes having a Writes implicit?]
Edit: How can I write the function input type so that it corresponds only to classes having a Writes implicit?
I tried this:
trait JsonWritableResult[T <: Product] {
implicit val jsonWrites: Writes[T]
}
case class Test(a: Int)
object Test extends JsonWritableResult[Test] {
implicit val jsonWrites: Writes[Test] = Json.writes[Test]
}
def myJson(cc: JsonWritableResult[_ <: Product]): JsValue = {
Json.toJson(cc)
}
But it says "No Json serializer found for type models.JsonWritableResult[_$2]".
Something like this seems to give you the behavior you want.
import play.api.libs.json.{JsValue, Json, Writes}
trait Product {}
case class Test(a: Int) extends Product
object Test {
implicit val jsonWrites: Writes[Test] = Json.writes[Test]
}
def myJson[T <: Product](cc: T)(implicit writes: Writes[T]): JsValue = {
Json.toJson(cc) // simplified
}
import Test._
myJson(Test(3))
This is not tested in the general case, but in a Worksheet it seems to work.
Instead of forcing the case class to have the implicit, it is more clever to force it to override a toJson method from a trait, using or not an implicit defined in the case class. Much simpler and it works. Then my factory can explicit this trait in its output type, and thus I can serialize whatever it outputs.
But since the other answer answered the question that I formulated wrong, I accept it ;)
Related
I have a problem with declaring of generic method in Play 2.6 application that converts JSON to instance of one of the case class models.
All models declared with helper objects and formatters:
import play.api.libs.json.{Json, OFormat}
case class Shot(id: Long, likes_count: Long)
object Shot {
implicit val format: OFormat[Shot] = Json.format[Shot]
}
val s1: Shot = Json.toJson(f).as[Shot] // Works great
def testJsonGeneric[T](js: JsValue)(implicit ev: OFormat[T]): T = {
js.as[T](ev)
}
val s2: Shot = testJsonGeneric(Json.toJson(f)) // could not find implicit value for parameter ev: play.api.libs.json.OFormat[T]. Compilation failed
The last line of code throws
could not find implicit value for parameter ev: play.api.libs.json.OFormat[T]
But if I call my generic method like this (with explicit formatter) it works just fine:
val s2: Shot = testJsonGeneric(Json.toJson(f))(Shot.format)
However, it looks like if I expect my JSON to return a list of objects I have to define an extra formatter for List[Shot] to pass explicitly to the method when default Play's json.as[List[Shot]] could easily allow me to do this with a single existing formatter like the one that already defined in the helper object.
So, is it even possible to provide formatters implicitly for generic type T in my case?
Thank you
You can definitely do this, you just have to change the declaration a bit.
Move the case class and companion declaration outside the method, and then explicitly import Shot._ to bring the implicit in scope:
import play.api.libs.json.{JsValue, Json, OFormat}
object Foo {
case class Shot(id: Long, likes_count: Long)
object Shot {
implicit def format: OFormat[Shot] = Json.format[Shot]
}
def main(args: Array[String]): Unit = {
import Shot._
val f = Shot(1, 2)
def testJsonGeneric[T](js: JsValue)(implicit ev: OFormat[T]): T = {
js.as[T](ev)
}
val s2: Shot = testJsonGeneric(Json.toJson(f))
}
}
I would like to design a base trait/class in Scala that can produce the following json:
trait GenericResource {
val singularName: String
val pluralName: String
}
I would inherit this trait in a case class:
case class Product(name: String) extends GenericResource {
override val singularName = "product"
override val pluralName = "products"
}
val car = Product("car")
val jsonString = serialize(car)
the output should look like: {"product":{"name":"car"}}
A Seq[Product] should produce {"products":[{"name":"car"},{"name":"truck"}]} etc...
I'm struggling with the proper abstractions to accomplish this. I am open to solutions using any JSON library (available in Scala).
Here's about the simplest way I can think of to do the singular part generically with circe:
import io.circe.{ Decoder, Encoder, Json }
import io.circe.generic.encoding.DerivedObjectEncoder
trait GenericResource {
val singularName: String
val pluralName: String
}
object GenericResource {
implicit def encodeResource[A <: GenericResource](implicit
derived: DerivedObjectEncoder[A]
): Encoder[A] = Encoder.instance { a =>
Json.obj(a.singularName -> derived(a))
}
}
And then if you have some case class extending GenericResource like this:
case class Product(name: String) extends GenericResource {
val singularName = "product"
val pluralName = "products"
}
You can do this (assuming all the members of the case class are encodeable):
scala> import io.circe.syntax._
import io.circe.syntax._
scala> Product("car").asJson.noSpaces
res0: String = {"product":{"name":"car"}}
No boilerplate, no extra imports, etc.
The Seq case is a little trickier, since circe automatically provides a Seq[A] encoder for any A that has an Encoder, but it doesn't do what you want—it just encodes the items and sticks them in a JSON array. You can write something like this:
implicit def encodeResources[A <: GenericResource](implicit
derived: DerivedObjectEncoder[A]
): Encoder[Seq[A]] = Encoder.instance {
case values # (head +: _) =>
Json.obj(head.pluralName -> Encoder.encodeList(derived)(values.toList))
case Nil => Json.obj()
}
And use it like this:
scala> Seq(Product("car"), Product("truck")).asJson.noSpaces
res1: String = {"products":[{"name":"car"},{"name":"truck"}]}
But you can't just stick it in the companion object and expect everything to work—you have to put it somewhere and import it when you need it (otherwise it has the same priority as the default Seq[A] instances).
Another issue with this encodeResources implementation is that it just returns an empty object if the Seq is empty:
scala> Seq.empty[Product].asJson.noSpaces
res2: String = {}
This is because the plural name is attached to the resource at the instance level, and if you don't have an instance there's no way to get it (short of reflection). You could of course conjure up a fake instance by passing nulls to the constructor or whatever, but that seems out of the scope of this question.
This issue (the resource names being attached to instances) is also going to be trouble if you need to decode this JSON you've encoded. If that is the case, I'd suggest considering a slightly different approach where you have something like a GenericResourceCompanion trait that you mix into the companion object for the specific resource type, and to indicate the names there. If that's not an option, you're probably stuck with reflection or fake instances, or both (but again, probably not in scope for this question).
I want to derive Encoder instances for value classes. With the semiauto mechanism I am not able to derive nested classes.
Image the following case class structure
{
case class Recipient(email: Recipient.Email, name: Recipient.Name)
object Recipient {
case class Email(value: String) extends AnyVal
case class Name(value: String) extends AnyVal
}
}
In an ammonite shell ( also add the Recipient case classes )
load.ivy("io.circe" %% "circe-core" % "0.6.1")
load.ivy("io.circe" %% "circe-generic" % "0.6.1")
import io.circe._
import io.circe.generic.semiauto._
import io.circe.syntax._
Now deriving a decoder for Email results as expected in
Recipient.Email("ab#cd.com").asJson(deriveEncoder[Recipient.Email])
Json = {
"value" : "ab#cd.com"
}
Deriving an Encoder[Recipient] doesn't work
deriveDecoder[Recipient]
could not find Lazy implicit value of type
io.circe.generic.decoding.DerivedDecoder[$sess.cmd5.Recipient]
What I would like to do is deriving an Encoder[Recipient.Email] that returns the wrapped type. This little piece works if I derive the codec explicitly.
import shapeless.Unwrapped
implicit def encodeAnyVal[W <: AnyVal, U](
implicit unwrapped: Unwrapped.Aux[W, U],
encoderUnwrapped: Encoder[U]): Encoder[W] = {
Encoder.instance[W](v => encoderUnwrapped(unwrapped.unwrap(v)))
}
Recipient.Email("ab#cd.com").asJson(encodeAnyVal[Recipient.Email, String])
res11: Json = "ab#cd.com"
Still I can't derive a Encoder[Recipient]
implicit val emailEncoder: Encoder[Recipient.Email] = encodeAnyVal[Recipient.Email, String]
implicit val nameEncoder: Encoder[Recipient.Name] = encodeAnyVal[Recipient.Name, String]
deriveDecoder[Recipient]
cmd14.sc:1: could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[$sess.cmd5.Recipient]
Has anyone done something similar?
Thanks in advance,
Muki
You should add implicit witness instance instead of type bound. Going to end up with something like this:
implicit def encodeAnyVal[W, U](
implicit ev: W <:< Anyval,
unwrapped: Unwrapped.Aux[W, U],
encoderUnwrapped: Encoder[U]): Encoder[W] = {
Encoder.instance[W](v => encoderUnwrapped(unwrapped.unwrap(v)))
}
I would like to serialize an extended class in scala and
i have some test code..
import org.specs2.mutable._
import org.specs2.runner._
import org.junit.runner._
import play.api.libs.json.Json
#RunWith(classOf[JUnitRunner])
class JsonSerializerTest extends Specification {
class A(val s1: String)
case class B(s2: String) extends A("a")
"Application" should {
"serialize class to JSON" in {
implicit val bWrites = Json.writes[B]
implicit val bReads = Json.reads[B]
val bClass = B("b")
println(bClass.s1 + " " + bClass.s2)
val serialized = Json.toJson[B](bClass)
val s1 = (serialized \ "s1").asOpt[String]
s1 should beSome[String]
}
}
}
In this case test print:
a b
Application should
'None' is not Some
java.lang.Exception: 'None' is not Some
It means that s1 field from parent class were not serialized.
The solution
class A(val s1: String)
case class B(override val s1: String, s2: String) extends A(s1)
mostly unacceptable because in real application classes have a lot of fields and specifying them explicitly every time when i extend class complicates the code.
Is there any other solution for this case?
you can manually create the json serializers (described here: https://www.playframework.com/documentation/2.5.x/ScalaJsonCombinators)
the problem with your version is that Json.writes and Json.reads are macros that are looking specifically at the constructor of the case class, and building the serializer from that (so superclass arguments aren't captured). you could copy and roll your own version of the macro: https://github.com/playframework/playframework/blob/d6c2673d91d85fd37de424951ee5ad9f4f4cce98/framework/src/play-json/src/main/scala/play/api/libs/json/JsMacroImpl.scala
lastly, you can make a function that takes the result of Json.writes and Json.reads, and adds on the shared fields you want. something like:
object A {
def writesSubclass[T](writer: Writes[T]): Writes[T] = new Writes[T] {
def writes(t: T) = Json.obj("s1" -> t.s1) ++ writer.writes(t).as[JsObject]
}
}
implicit val bWrites = A.writesSubclass(Json.writes[B])
depending on how often your A gets extended, this might be your best bet.
Hello I am bit struggling with parsing a json with spray-json libary whith recursive data structure.
I have following case class structure and Protocol defined
import spray.json.DefaultJsonProtocol
import spray.json._
case class OfferAnalysisReport(BUG: DleTN, DOM: DleTN) extends AnalyticalReport
case class DleTN(doc_count: Int, result: AggsDefinition)
case class BucketDefinition(key: String, doc_count: Int, result: Option[AggsDefinition])
case class AggsDefinition(buckets: List[BucketDefinition])
object OfferAnalysisReportProtocol extends DefaultJsonProtocol {
implicit val aggsDefFormat = lazyFormat(jsonFormat(AggsDefinition,"buckets"))
implicit val bucketDefinitionFormat = lazyFormat(jsonFormat(BucketDefinition,"key","doc_count","result"))
implicit val dleTypNemovitostisFormat = jsonFormat2(DleTypuNemovitosti)
implicit val OfferAnalysisReportFormat = jsonFormat2(OfferAnalysisReport)
}
In the test I am importing:
import spray.json._
import OfferAnalysisReportProtocol._
But I am still getting
Error: couldn't find implicit value for evidence parameter of type BuckedDefinition.
Am I missing something important here? Could someone give me a hint?
You will have to wrap your format constructor with lazyFormat as you have done, but you also have to supply an explicit type annotation like this:
implicit val fooFormat: JsonFormat[Foo] = lazyFormat(jsonFormat(Foo, "i", "foo"))
Source: https://github.com/spray/spray-json#jsonformats-for-recursive-types
#spydons solution did not work for me. You can handle recursive classes by writing a custom serializer as well. Here's an example for writing:
object Test extends App{
import spray.json._
case class Foo(i: Int, foo: Foo)
implicit object FooJsonFormat extends RootJsonWriter[Foo] {
override def write(c: Foo) : JsValue =
if (c.foo != null) { JsObject("i" -> JsNumber(c.i),"foo" -> write(c.foo)) }
else { JsObject("i" -> JsNumber(c.i)) }
}
println(Foo(20,Foo(10,null)).toJson) //prints {"foo":{"i":10},"i":20}
}