I have defined a case class User(id : String, name : String)
I have defined both json encoders and decoders as :
import io.circe.generic.extras.semiauto.{deriveConfiguredCodec, deriveConfiguredDecoder, deriveConfiguredEncoder}
implicit val userEncoder: Encoder[User] = deriveConfiguredEncoder
implicit val userDecoder: Decoder[User] = deriveConfiguredDecoder
I need to also define the encoder/decoder for val res: List[User]
is there something built in circe besides using something explicit ?
Related
In playframework I am trying to write the reads/writes for JSON, along with reactivemongo's BSON handlers for my case class:
import play.api.libs.json._
import reactivemongo.api.bson._
case class SomeThing[T](id: String, name: String, value: T)
object SomeThing {
implicit val stWrites = Json.writes[SomeThing]
implicit val stReads = Json.reads[SomeThing]
implicit val stHander = Macros.handler[SomeThing]
}
Because my case class as a type parameter T, I am getting this compile error currently:
class SomeThing takes type parameters
How can I solve this issue?
You need implicit polymorphic functions:
import play.api.libs.json._
import reactivemongo.api.bson._
case class SomeThing[T](id: String, name: String, value: T)
//defined as polymorphic functions
//type T in each case needs to be accompanied by some implicit evidence
implicit def stWrites[T: OWrites]: OWrites[SomeThing[T]] =
Json.writes[SomeThing[T]]
implicit def stReads[T: Reads]: Reads[SomeThing[T]] =
Json.reads[SomeThing[T]]
implicit def stHander[T: BSONDocumentHandler]: BSONDocumentHandler[SomeThing[T]] =
Macros.handler[SomeThing[T]]
stReads[String] //this works because play has an implicit StringReads available
case class OtherThing()
stReads[OtherThing] //this doesn't work we need to define a Reads[OtherThing]
implicit val otReads: Reads[OtherThing] =
Json.reads[OtherThing]
stReads[OtherThing] //now it works
Later Edit: I dropped the generic example
I know there is an xpath like api to get fields out of a JObject in Json4s
val x = (obj \ "Type").asInstanceOf[JString].values
However it feels a bit cumbersome and I'm not a fan of symbolic like apis. I kind of want something like this:
implicit class JsonExtensions(json: JObject) {
def get[T <: JValue](key: String) : T.Values = {
(json \ key).asInstanceOf[T].values
}
}
and use it something like this
val x = obj.get[String]("type")
However it doesn't compile, T's upper bound is a JValue so Id expect to be able to reference the type member Values which is on all JValues. For reference his is a snipper of JValue:
sealed abstract class JValue extends Diff.Diffable with Product with Serializable {
type Values
def values: Values
...
}
I'm new to scala, how can i make the compiler happy?
Here is the solution if anyone is interested:
implicit class JsonExtensions(json: JValue) {
def get[T <: JValue](key: String) : T#Values = {
(json \ key).asInstanceOf[T].values
}
}
val x = obj.get[JString]("type")
You have to specify the expected type but x will the underlying Values of the Json Ast type. So for JString is a string, for JObject its a Map[String,Any]
Thank you insan-e
I'm using play-json to map Json to case classes or enums. I'm looking for a smart way of creating Formats implicitly, since my project contains many types definitions.
At the moment I created a simple function to generate Formats for Enums:
def formatEnum[E <: Enumeration](enum: E) = Format(Reads.enumNameReads(enum), Writes.enumNameWrites)
But it takes a non-implicit argument so it cannot be used as implicit converter.
I tried to do the same for case classes:
implicit def caseFormat[A] = Json.format[A]
But I get the error "No unapply or unapplySeq function found", since Json.format is a macro which inspect the structure of the class.
Then I tried to create my macro in this way:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
implicit def caseFormat[A](): Format[A] = macro impl[A]
def impl[A: c.WeakTypeTag](c: Context)(): c.Expr[Reads[A]] = {
import c.universe._
val TypeRef(pre, sym, args) = weakTypeTag[A].tpe
val t = args.head
val expr = q"Json.format[$t]"
c.Expr[Reads[A]](expr)
}
But the compiler does not find the implicit Format, though there is an implicit def that should generate the value.
Of course I can simply define many implicit val, but I think there is a smarter way to do it.
Assuming you have lots of case classes and you wish to json serialize it on the fly without having to write a play-json writer.
import play.api.libs.json._
import scala.reflect.runtime.{universe => ru}
implicit class writeutil[T: ru.TypeTag](i: T) {
implicit val writer = Json.writes[T]
def toJson() = Json.toJson(i)
}
def toInstance[T: ru.TypeTag](s: String): Option[T] = {
implicit val reader = Json.reads[T]
Json.fromJson[T](Json.parse(s)) match {
case JsSuccess(r: T, path: JsPath) => Option(r)
case e: JsError => None
}
}
An optimal implementation would be to re-use the reader/writer by caching and lookup. You can also read more about play-json.
You can use this as:
case class Entity(a: String, b: Int)
val e = Entity("Stack", 0)
e.toJson()
Here is what I am trying to do
case class MessageModel (time: Long, content: String) {}
val message = MessageModel(123, "Hello World")
def jsonParser[A] (obj: A) : String = obj.asJson.noSpaces
println jsonParser[MessageModel](message)
this doesn't work, because it will complain
Error:(13, 8) could not find implicit value for parameter encoder: io.circe.Encoder[A] obj.asJson.noSpaces
^
I kind of understand why it is happening, but is there a way to work around it?
Thanks
Encoding and decoding in circe are provided by type classes, which means that you have to be able to prove at compile time that you have a type class instance for A if you want to encode (or decode) a value of type A.
This means that when you write something like this:
import io.circe.syntax._
def jsonPrinter[A](obj: A): String = obj.asJson.noSpaces
You're not providing enough information about A for circe to be able to print values of that type. You can fix this with a context bound:
import io.circe.Encoder
import io.circe.syntax._
def jsonPrinter[A: Encoder](obj: A): String = obj.asJson.noSpaces
Which is Scala's syntactic sugar for something like this:
def jsonPrinter[A](obj: A)(implicit encoder: Encoder[A]): String =
obj.asJson.noSpaces
Both of these will compile, and you can pass them a value of any type that has an implicit Encoder instance. For your MessageModel specifically, you could use circe's generic derivation, since it's a case class:
scala> import io.circe.generic.auto._
import io.circe.generic.auto._
scala> case class MessageModel(time: Long, content: String)
defined class MessageModel
scala> val message = MessageModel(123, "Hello World")
message: MessageModel = MessageModel(123,Hello World)
scala> jsonPrinter(message)
res0: String = {"time":123,"content":"Hello World"}
Note that this wouldn't work without the auto import, which is providing Encoder instances for any case class (or sealed trait hierarchy) whose members are all also encodeable.
I want to be able to create case classes in Scala using spray-json, but define an asJson method in the class , but I can't seem to figure out how. For example, I'd like to be able to do this:
case class Foo(bar: String) {
def toJson: JsValue = ...
}
It would be easy enough to create an implicit JSON converter:
object JsonProtocols extends DefaultJsonProtocol {
implicit val fooFormat = jsonFormat1(Foo)
}
But as far as I know, this can only be done outside of the class. I would love to find a way of declaring a JSON format and convert to JSON within the class itself.
You could imagine doing this:
scala> import spray.json._
import spray.json._
scala> case class Foo(bar: String) {
def toJson:JsValue = JsObject( "bar" -> JsString(bar) )
}
defined class Foo
scala> Foo("bar").toJson
res2: spray.json.JsValue = {"bar":"bar"}
So far so good, but that doesn't fit into Spray's typeclass mechanism. Spray's routing DSL, for example, would give you a type error if you then tried to convert a Foo to/from a JsValue (e.g. using the route entity( as[Foo] ) { ... } ). Nor could the implicits they have already prepared for you, for types like List and Set, work with Foo:
scala> import DefaultJsonProtocol._
import DefaultJsonProtocol._
scala> List(Foo("bar")).toJson
<console>:31: error: Cannot find JsonWriter or JsonFormat type class for List[Foo]
List(Foo("bar")).toJson
because there is no JsonFormat class for them to use to convert a Foo, like the one that JsonFormat1(Foo) would have created.
You might then think to put the format inside the Foo companion object, since an in-scope class's companion object is on the implicit search path, like so:
object Foo extends DefaultJsonProtocol {
implicit val fooFormat = jsonFormat1(Foo)
}
case class Foo(bar: String)
But because we're not finished defining Foo at that point, the compiler gives us a type error:
[error] found : Foo.type
[error] required: ? => ?
[error] Note: implicit value fooFormat is not applicable here because it comes after the application point and it lacks an explicit result type
Adding an explicit result type of RootJsonFormat[Foo] doesn't solve the problem:
[error] found : Foo.type
[error] required: ? => Foo
[error] implicit val fooFormat:RootJsonFormat[Foo] = jsonFormat1(Foo)
The trick (thanks knutwalker!) is to explicitly pass Foo.apply:
object Foo extends DefaultJsonProtocol {
implicit val fooFormat = jsonFormat1(Foo.apply)
}
case class Foo(bar: String)