How to use nested case classes and spray json implicits - json

I am trying to convert case classes to json with spray.io json. The code below:
case class Value(amt: Int)
case class Item(name: String, count: Value)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val itemFormat = jsonFormat2(Item)
}
import MyJsonProtocol._
import spray.json._
val json = Item("mary", Value(2)).toJson
println(json)
gives:
could not find implicit value for evidence parameter of type onextent.bluecase.examples.ex1.ExampleJson2.MyJsonProtocol.JF[Value]
I've tried to define a JsonProtocol for Value as well but get same. Searching stackoverflow I only see this error related to generics, which this is not.
What am I missing? (re-reading about implicits now...)

You need a json format for your Value class as it is part of your Item class. So your object would need to look like this:
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val valueFormat = jsonFormat1(Value)
implicit val itemFormat = jsonFormat2(Item)
}

The answer mentioned by StuartMcvean is correct. However I just want to add a note that the order in which you declare the implicits is important. For example the below declaration will still result in the same failure.
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val itemFormat = jsonFormat2(Item)
implicit val valueFormat = jsonFormat1(Value)
}

Related

circe encoding putting :: when a list of case class from trait is not sealed

Hello everyone I am facing a problem with the circe library to translate between json and at scala case class any help will be highly appreciate.
in the past i have an ADT like this.
sealed trait Sons
case class Son1(name: String, belongings: List[String]) extends Sons
case class Son2(lastName: String, belongings: List[String]) extends Sons
and have no problems whatsoever but now because of the design the file in witch this trait is, is costly to change we have to remove the sealed from the trait so now we have this (with the sons in different files/libraries)
trait Sons
case class Son1(name: String, belongings: List[String]) extends Sons
case class Son2(lastName: String, belongings: List[String]) extends Sons
when trying to convert a list of sons from Scala to json the library puts :: before the list generating a problem as can be seen in this example.
object Test extends App{
implicit val encoderSon1: Encoder[Son1]=deriveEncoder[Son1]
implicit val decoderSon1: Decoder[Son1]=deriveDecoder[Son1]
implicit val encoderSon2: Encoder[Son2]=deriveEncoder[Son2]
implicit val decoderSon2: Decoder[Son2]=deriveDecoder[Son2]
implicit val encoderSon: Encoder[Sons] = Encoder.instance {
case son1: Son1 => son1.asJson
case son2: Son2 => son2.asJson
}
implicit val DecoderSon: Decoder[Sons] =
List[Decoder[Sons]](
Decoder[Son1].widen,
Decoder[Son2].widen
).reduceLeft(_ or _)
implicit val encoderSonList: Encoder[List[Sons]] = deriveEncoder
implicit val DecoderSonList: Decoder[List[Sons]] = deriveDecoder
val sonsList: List[Sons] = List(Son1("James",List()),Son2("Mike",List()))
println(s"1->${sonsList}")
println(s"2->${sonsList.asJson.noSpaces}")
val andBack=decode[List[Sons]](sonsList.asJson.noSpaces).fold(fa=>fa.getMessage,fb=>fb.toString())
println(s"3->${andBack}")
}
the prints are printint
1->List(Son1(James,List()), Son2(Mike,List()))
2->{"::":[{"name":"James","belongings":[]},{"lastName":"Mike","belongings":[]}]}
3->Attempt to decode value on failed cursor: DownField(head),DownField(::)
the problem is of course i do not want the list to be put in a field called :: when converting to JSON.
thank you for any help possible :D
Don't use semi auto for List - semiauto uses slightly different mechanism than other derivations to e.g. avoid issues like:
implicit val x: X = implicitly[X] // uses val x, so we have cyclic dependency = error in runtime
For that reason when you are using semiauto here, List instances from Encoder and Decoder are ignored, and instead macro generates code for List ADT - that is case class :: and case object Nil.
If you remove
implicit val encoderSonList: Encoder[List[Sons]] = deriveEncoder
implicit val DecoderSonList: Decoder[List[Sons]] = deriveDecoder
it will work again.

How can I define a dynamic base json object?

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).

Scala Play: how to define a "Json writable class"

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 ;)

spray-json recursive json issue - could not find implicit value for evidence parameter

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}
}

How setup spray-json to set null when json element is not present?

Here is spray-json example. Here is NullOptions trait.
The problem is when I declare a case class say
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val some: RootJsonFormat[Some] = jsonFormat2(Some)
}
case class Some (
name:String,
age:Int
)
and json do not contains a field for example:
{
"name":"John"
}
I get: java.util.NoSuchElementException: key not found: age
So I have to add an Option and NullOption trait like that:
object MyJsonProtocol extends DefaultJsonProtocol with NullOptions {
implicit val some: RootJsonFormat[Some] = jsonFormat2(Some)
}
case class Some (
name:String,
age:Option[Int]
)
Everything works. But I do not want to have a case classes where all member are Option. Is there a way to configure spray json unmarshalling to just set nulls without additional Option type?
P.S.
I understand that in general Option is better then null check, but in my case it is just monkey code.
Also complete example of marshalling during response processing is here
The only way I can think of is to implement your own Protocol via read/write, which might be cumbersome. Below is a simplified example. Note that I changed the age to be an Integer instead of an Int since Int is an AnyVal, which is not nullable by default. Furthermore, I only consider the age field to be nullable, so you might need to adopt as necessary. Hope it helps.
case class Foo (name:String, age: Integer)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object FooJsonFormat extends RootJsonFormat[Foo] {
def write(foo: Foo) =
JsObject("name" -> JsString(foo.name),
"age" -> Option(foo.age).map(JsNumber(_)).getOrElse(JsNull))
def read(value: JsValue) = value match {
case JsObject(fields) =>
val ageOpt: Option[Integer] = fields.get("age").map(_.toString().toInt) // implicit conversion from Int to Integer
val age: Integer = ageOpt.orNull[Integer]
Foo(fields.get("name").get.toString(), age)
case _ => deserializationError("Foo expected")
}
}
}
import MyJsonProtocol._
import spray.json._
val json = """{ "name": "Meh" }""".parseJson
println(json.convertTo[Foo]) // prints Foo("Meh",null)
It seems you're out of luck
From the doc you linked:
spray-json will always read missing optional members as well as null optional members as None
You can customize the json writing, but not the reading.