Error on writing a json formatter in a Play application - json

i'm writing a Play 2.3.2 application in Scala.
I've write a class that represents the correlation between a category and an attribute in my system.
The class is declared like the following:
case class Correlation(val category: String, val attribute: String,
val value: Double, val weight: Double)
Now i'm writing a json formatter but i'm getting some errors.
The Formatter is write like the following:
object CorrelationFormatters {
implicit val storageFormatter: Format[Correlation] = {
val correlationReads: Reads[Correlation] = (
(__ "category").read[String] and
(__ "attribute").read[String] and
(__ "value").read[Double] and
(__ "weight").read[Double]
)(Correlation.apply _)
val correlationWrites: Writes[Correlation] = (
(__ "category").write[String] and
(__ "attribute").write[String] and
(__ "value").write[Double] and
(__ "weight").write[Double]
)(unlift(Correlation.unapply _))
}
}
But i'm getting the following compiler errors:
error] /Users/alberto/git/bdrim/modules/recommendation-system/app/recommendationsystem/formatters/json/CorrelationFormatters.scala:19: ')' expected but string literal found.
[error] (__ "category").read[String] and
[error] ^
[error] /Users/alberto/git/bdrim/modules/recommendation-system/app/recommendationsystem/formatters/json/CorrelationFormatters.scala:31: ')' expected but '}' found.
[error] }
[error] ^
[error] /Users/alberto/git/bdrim/modules/recommendation-system/app/recommendationsystem/formatters/json/CorrelationFormatters.scala:19: ')' expected but string literal found.
[error] (__ "category").read[String] and
[error] ^
[error] /Users/alberto/git/bdrim/modules/recommendation-system/app/recommendationsystem/formatters/json/CorrelationFormatters.scala:31: ')' expected but '}' found.
[error] }
[error] ^
[error] two errors found
What's wrong??
I've used the ScalaJsonCombinators tutorial like example, but i can't find out where i'm wrong.

Try with:
object Correlation {
implicit val jsonModelReads = Json.reads[Correlation]
implicit val jsonModelWrites = Json.writes[Correlation]
implicit val jsonModelFormat = Json.format[Correlation]
}
And then import Correlation._

Very simple fix:
(__ "category").read[String]
should be
(__ \ "category").read[String]
And for all the other lines as well..

Related

Scala json parsing for single field class

I have following class and Reads Writes for json parsing / writeing
import play.api.libs.json._
import play.api.libs.functional.syntax._
import scala.collection.immutable.{Seq => ISeq, Set => ISet}
case class FileExcInc( isExclude: Boolean, isFile: Boolean, pattern: String) { }
implicit val fEIReads: Reads[FileExcInc] = (
(__ \ "isExclude").read[Boolean] and
(__ \ "isFile").read[Boolean] and
(__ \ "pattern").read[String]
)(FileExcInc.apply _)
implicit val fEIWrites: Writes[FileExcInc] = (
(__ \ "isExclude").write[Boolean] and
(__ \ "isFile").write[Boolean] and
(__ \ "pattern").write[String]
)(unlift(FileExcInc.unapply))
case class DirConf(sourceDir: String, csdataFile: String, fileExcIncSeq: ISeq[FileExcInc]) { }
implicit val dCReads: Reads[DirConf] = (
(__ \ "sourceDir").read[String] and
(__ \ "csdataFile").read[String] and
(__ \ "fileExcIncSeq").read[ISeq[FileExcInc]]
)(DirConf.apply _)
implicit val dCWrites: Writes[DirConf] = (
(__ \ "sourceDir").write[String] and
(__ \ "csdataFile").write[String] and
(__ \ "fileExcIncSeq").write[ISeq[FileExcInc]]
)(unlift(DirConf.unapply))
This compiles OK, but the following does not:
case class Conf(dirConfSeq: ISeq[DirConf]) { }
implicit val cReads: Reads[Conf] = (
(__ \ "dirConfSeq").read[ISeq[DirConf]]
)(Conf.apply _)
implicit val cWrites: Writes[Conf] = (
(__ \ "dirConfSeq").write[ISeq[DirConf]]
)(unlift(Conf.unapply))
It produces following error:
[info] Compiling 17 Scala sources and 1 Java source to /home/jk/workspace/Gen/target/scala-2.12/classes...
[error] /home/jk/workspace/Gen/src/main/scala/com/example/CTool.scala:341: overloaded method value read with alternatives:
[error] (t: scala.collection.immutable.Seq[com.example.CTool.DirConf])play.api.libs.json.Reads[scala.collection.immutable.Seq[com.example.CTool.DirConf]] <and>
[error] (implicit r: play.api.libs.json.Reads[scala.collection.immutable.Seq[com.example.CTool.DirConf]])play.api.libs.json.Reads[scala.collection.immutable.Seq[com.example.CTool.DirConf]]
[error] cannot be applied to (scala.collection.immutable.Seq[com.example.CTool.DirConf] => com.example.CTool.Conf)
[error] (__ \ "dirConfSeq").read[ISeq[DirConf]]
[error] ^
[error] /home/jk/workspace/Gen/src/main/scala/com/example/CTool.scala:345: overloaded method value write with alternatives:
[error] (t: scala.collection.immutable.Seq[com.example.CTool.DirConf])(implicit w: play.api.libs.json.Writes[scala.collection.immutable.Seq[com.example.CTool.DirConf]])play.api.libs.json.OWrites[play.api.libs.json.JsValue] <and>
[error] (implicit w: play.api.libs.json.Writes[scala.collection.immutable.Seq[com.example.CTool.DirConf]])play.api.libs.json.OWrites[scala.collection.immutable.Seq[com.example.CTool.DirConf]]
[error] cannot be applied to (com.example.CTool.Conf => scala.collection.immutable.Seq[com.example.CTool.DirConf])
[error] (__ \ "dirConfSeq").write[ISeq[DirConf]]
What's wrong in the last Reads, Writes? How to create Reads, Writes for class having just one field?
To create a Reads/Writes for a case class with just one field, use map and contramap to convert the field to and from the case class.
implicit val cReads: Reads[Conf] =
(__ \ "dirConfSeq").read[ISeq[DirConf]].map(Conf)
implicit val cWrites: Writes[Conf] =
(__ \ "dirConfSeq").write[ISeq[DirConf]].contramap(_.dirConfSeq)
Even easier is to use the Json.format macro to generate all of the Reads/Writes boilerplate. It handles case classes with any number of fields provided the field names match the JSON. A Format[T] is both a Reads[T] and a Writes[T].
implicit val fEIFormat: Format[FileExcInc] = Json.format[FileExcInc]
implicit val dCFormat: Format[DirConf] = Json.format[DirConf]
implicit val cFormat: Format[Conf] = Json.format[Conf]
See: https://www.playframework.com/documentation/2.6.x/ScalaJsonAutomated

No Json serializer as JsObject found for type reactivemongo.play.json.JSONSerializationPack.type

I am using play framework 2.5.3 with reactive mongoDB.
import javax.inject._
import model._
import play.api.Logger
import play.api.libs.json._
import play.api.mvc._
import play.modules.reactivemongo._
import reactivemongo.api.ReadPreference
import reactivemongo.play.json._
import reactivemongo.play.json.collection._
import scala.concurrent.{ExecutionContext, Future}
class InsertController #Inject()(val reactiveMongoApi: ReactiveMongoApi)(implicit exec: ExecutionContext) extends Controller with MongoController with ReactiveMongoComponents {
def dataFuture: Future[JSONCollection] = database.map(_.collection[JSONCollection]("data"))
def createFromJson = Action.async(parse.json) { request =>
Json.fromJson[jsonWrapper](request.body) match {
case JsSuccess(data, _) =>
for {
data <- dataFuture
lastError <- data.insert(data)
} yield {
Logger.debug(s"Successfully inserted with LastError: $lastError")
Ok("Inserted into db")
}
case JsError(errors) =>
Future.successful(BadRequest("Something went wrong"))
}
}
Here is my controller and when compiling i get the following exception:
[info] Compiling 6 Scala sources and 1 Java source to /home/***/target/scala-2.11/classes...
[error] /home/***/app/controllers/InsertController.scala:38: No Json serializer as JsObject found for type reactivemongo.play.json.JSONSerializationPack.type. Try to implement an implicit OWrites or OFormat for this type.
[error] lastError <- data.insert(data.pack)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
[info] Compiling 6 Scala sources and 1 Java source to /home/***/target/scala-2.11/classes...
[error] /home/***/app/controllers/InsertController.scala:38: No Json serializer as JsObject found for type reactivemongo.play.json.JSONSerializationPack.type. Try to implement an implicit OWrites or OFormat for this type.
[error] lastError <- data.insert(data.pack)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] application -
! #705di1397 - Internal server error, for (GET) [/] ->
play.sbt.PlayExceptions$CompilationException: Compilation error[No Json serializer as JsObject found for type reactivemongo.play.json.JSONSerializationPack.type. Try to implement an implicit OWrites or OFormat for this type.]
at play.sbt.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:27)
at play.sbt.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:27)
at scala.Option.map(Option.scala:145)
at play.sbt.run.PlayReload$$anonfun$taskFailureHandler$1.apply(PlayReload.scala:49)
at play.sbt.run.PlayReload$$anonfun$taskFailureHandler$1.apply(PlayReload.scala:44)
at scala.Option.map(Option.scala:145)
at play.sbt.run.PlayReload$.taskFailureHandler(PlayReload.scala:44)
at play.sbt.run.PlayReload$.compileFailure(PlayReload.scala:40)
at play.sbt.run.PlayReload$$anonfun$compile$1.apply(PlayReload.scala:17)
at play.sbt.run.PlayReload$$anonfun$compile$1.apply(PlayReload.scala:17)
It recommends writing a Owrites or OFormat for JSONCollection which is part of the reactivemongo.play.json._ package and should already have these defined to my understanding.
Here is my jsonWrapper class:
case class jsonWrapper(tables : tables, userId : String)
object jsonWrapper{
implicit val jsonRead: Reads[jsonWrapper] = (
(JsPath \ "tables").read[tables] and
(JsPath \ "userID").read[String]
)(jsonWrapper.apply _)
implicit val jsonWrites: Writes[jsonWrapper] = (
(JsPath \ "tables").write[tables] and
(JsPath \ "userID").write[String]
)(json => (json.tables, json.userId))
implicit val jsonWrapperFormat : Format[jsonWrapper] = Json.format[jsonWrapper]
}
The tables class also has implictly defined format, Reads and writes.
I orignally used this example to get started:
https://github.com/jonasanso/play-reactive-mongo-db#master, which works but when i try to adapt it to my needs (i.e. with my jsonWrapper class) i get this error and do not understand why it is not working.
Many Thanks,
Peter M.
I Found my error.
def createFromJson = Action.async(parse.json) { request =>
Json.fromJson[jsonWrapper](request.body) match {
case JsSuccess(data, _) =>
for {
data <- dataFuture
lastError <- data.insert(data)
} yield {
Logger.debug(s"Successfully inserted with LastError: $lastError")
Ok("Inserted into db")
}
case JsError(errors) =>
Future.successful(BadRequest("Something went wrong"))
}
My case entry initiates an object named "data" which i follow up by overriding with my dataFuture object. Thus, causing the error. I simply had to change the variable names...
I fell kind of silly.

JSON implicit Reads/Writes in Playframerowk with one element not working

Say I have to classes that I will be using it for read and write JSON
case class OrganizationData(var kind: Option[String],var id: Option[String],var organizationReference: OrganizationReferenceData,var objectHash: Option[String],var friendlyName: Option[String])
case class OrganizationReferenceData(var organizationId:String)
The following are the read and write
implicit val organizationWrites = (
(__ \ "kind").writeNullable[String] and
(__ \ "id").writeNullable[String] and
(__ \ "organizationReference").write[OrganizationReferenceData] and
(__ \ "objectHash").writeNullable[String] and
(__ \ "friendlyName").writeNullable[String]
) ( unlift( OrganizationData.unapply ) )
implicit val OrganizationReferenceDataWrites:Writes[OrganizationReferenceData] = (
(__ \ "organizationId").write[String]
) ( unlift( OrganizationReferenceData.unapply ) )
when I try to compile this I am getting the following error.
Error:(54, 34) overloaded method value write with alternatives:
(t: String)(implicit w: play.api.libs.json.Writes[String])play.api.libs.json.OWrites[play.api.libs.json.JsValue] <and>
(implicit w: play.api.libs.json.Writes[String])play.api.libs.json.OWrites[String]
cannot be applied to (config.core.OrganizationReferenceData => String)
(__ \ "organizationId").write[String]
^
Am I missing some thing here? One weird thing that I have seen is if I add another field in "OrganizationReferenceDataWrites" class and have the write element it compiles. SO if we cannot have a single element that what is the best practice to do it?
I'm not sure why you are getting that message, but an alternative way to do it may be:
implicit val OrganizationReferenceDataWrites = new Writes[OrganizationReferenceData] {
def writes(organizationReferenceData: OrganizationReferenceData) = Json.obj(
"organizationId" -> organizationReferenceData.organizationId)
}
Caveat: I don't know my combinators well enough to know if this is exactly equivalent.
BTW Are you using mutable (var) variables for a reason?

Play Json add field which is not present in class

Let's say I have a class like this:
abstract class SomeSuperClass(name: String)
case class SomeClass(someString: String, opt: Option[String]) extends SomeSuperClass("someName")
I want to serialize this class and be able to add the name field, this was my first approach:
implicit def serialize: Writes[SomeClass] = new Writes[SomeClass] {
override def writes(o: SomeClass): JsValue = Json.obj(
"someString" -> o.someString,
"opt" -> o.opt,
"name" -> o.name
)
}
This returns null if there's a None, so I changed my implementation following the documentation to this:
implicit def serialize: Writes[SomeClass] = (
(JsPath \ "someString").write[String] and
(JsPath \ "opt").writeNullable[String] and
(JsPath \ "name").write[String]
)(unlift(SomeClass.unapply))
This doesn't compile, it works only if I remove the name field:
[error] [B](f: B => (String, Option[String], String))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.OWrites])play.api.libs.json.OWrites[B] <and>
[error] [B](f: (String, Option[String], String) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.OWrites])play.api.libs.json.OWrites[B]
[error] cannot be applied to (api.babylon.bridge.messaging.Command.SomeClass => (String, Option[String]))
[error] (JsPath \ "opt").writeNullable[String] and
How can I add a field which is not strictly present in a case class and has an optional field?
I'm using play-json 2.3.0.
Instead of using the default (compiler generated) unapply method, which knows nothing about your inherited values, you can write your own extractor for the JSON writes which takes a SomeClass instance and returns a (String, Option[String], String) tuple, i.e:
implicit def serialize: Writes[SomeClass] = (
(JsPath \ "someString").write[String] and
(JsPath \ "opt").writeNullable[String] and
(JsPath \ "name").write[String]
)(s => (s.someString, s.opt, s.name))
This gives you:
Json.toJson(SomeClass("foo", None))
// {"someString":"foo","name":"someName"}
Jspm.toJson(SomeClass("foo", Some("bar")))
// {"someString":"foo","opt":"bar","name":"someName"}

implicit json read and writes for lists

I am following the following example https://github.com/leon/play-salat/tree/master/sample . In my data model I have lists and I tried to adopt the model to that.
I get the following compilation errors:
[error] /home/malte/workspace/bdt/app/models/Ballot.scala:26: No Json deserializer found for type List[models.Position]. Try to implement an implicit Writes or Format for this type.
[error] "positions" -> b.positions,
[error] ^
[error] /home/malte/workspace/bdt/app/models/Ballot.scala:33: No Json deserializer found for type List[models.Position]. Try to implement an implicit Reads or Format for this type.
[error] (__ \ 'values).read[List[Position]]).map { l => l.getOrElse(Nil) } ~
For the type "Position" I have implicit reads and writes, but how to make it for List[Position] ? My implicit write and read:
implicit val ballotJsonWrite = new Writes[Ballot] {
def writes(b: Ballot): JsValue = {
Json.obj(
"positions" -> b.positions,
"title" -> b.title)
}
}
implicit val ballotJsonRead = (
(__ \ 'positions).readNullable(
(__ \ 'values).read[List[Position]]).map { l => l.getOrElse(Nil) } ~
(__ \ 'title).read[String])(Ballot.apply _)
For the read I followed Create implicit json read for List collection which might be missing from input json , but I get the above mentioned error.
actually now I found out that in the latest versions of salat. Json is automatically used to move between models and json objects. I removed the whole part and no it runs smoothly.
https://github.com/novus/salat/wiki/JSON