illegal start of simple expression Scala - json

I have two version of Scala compiler 2.11.12 and 2.12.4. I have a code which overrides read and write method of a json protocol. The code is something like this.
implicit lazy val abcJsonProtocol: RootJsonFormat[XYZ] = new
RootJsonFormat[XYZ] {
override def write(obj: XYZ): JsValue = {
val baseJson =
JsObject(
“abc” -> obj.efg.toJson,
“ikj” -> obj.mno.toJson
)
obj
.map(value => JsObject(baseJson.fields + (“wxy” ->
value.toJson)))
.getOrElse(baseJson)
}
override def read(value: JsValue): XYZ = {
val jsObject = value.asJsObject
jsObject.getFields("abc",
"kJ") match {
case Seq(efg, mno) =>
XYZ(
efg = efg.convertTo[String],
mno = mno.convertTo[String],
)
}
}
}
The error is something likes this
illegal start of simple expression
[error] )
[error] ^
')' expected but '}' found.
[error] }
[error] ^
The compilation fails only in 2.11.12 version and passes in the latter version
Thanks in advance.

Your case class
case class XYZ(efg: String, mno: String)
then, define the protocol something like this.
import spray.json._
implicit object XYZFormat extends RootJsonFormat[XYZ] {
// writer function
def write(obj: XYZ): JsValue = JsObject(
"efg" -> JsString(obj.efg),
"mno" -> JsString(obj.mno)
)
// reader function
def read(json: JsValue): XYZ =
json.asJsObject.getFields("efg", "mno") match {
case Seq(JsString(efg), JsString(mno)) => XYZ(efg, mno)
case _ => throw DeserializationException("Not valid XYZ model")
}
}

Related

validate json with type value

I wand to validate json that has a type value
I have a case class SearchRequestMessage with a companion object with json writer
Value T can be Long or UUID
case class SearchRequestMessage[T](jobId: UUID, offerId: UUID, search: T, searchByType: SearchByType)
object SearchRequestMessage{
implicit def searchResultsWrites[T: Writes]: Writes[SearchRequestMessage[T]] = new Writes[SearchRequestMessage[T]] {
def writes(searchSesult: SearchRequestMessage[T]) =
JsObject(
Seq(
"jobId" -> JsString(searchSesult.jobId.toString),
"offerId" -> JsString(searchSesult.offerId.toString),
"search" -> JsString(searchSesult.search.toString),
"searchByType" -> JsString(searchSesult.searchByType.toString)
))
}
}
Also I have SearchByTypes enum
object SearchByTypes extends Enumeration {
type SearchByType = Value
val Emails: SearchByType = Value(0, "EMAILS")
val Offer: SearchByType = Value(1, "OFFER")
implicit val SearchByTypeFormat: Format[SearchByTypes.Value] = JsonFormatter.enumFormat(SearchByTypes)
}
enum formatter
import play.api.libs.json._
object JsonFormatter {
def enumFormat[T <: Enumeration](enum: T): Format[T#Value] = new EnumFormatter[T](enum)
def enumWithIdsFormat[T <: Enumeration](enum: T): Format[T#Value] = new EnumWithIdsFormatter[T](enum)
class EnumFormatter[T <: Enumeration](enum: T) extends Format[T#Value] {
override def writes(o: T#Value): JsValue = o match {
case null => JsNull
case _ => JsString(o.toString)
}
override def reads(json: JsValue): JsResult[T#Value] = json match {
case JsString(value) => {
try {
JsSuccess(enum.withName(value))
} catch {
case _: NoSuchElementException =>
JsError(
s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$value'")
}
}
case _ => JsError(s"Invalid JSON: $json. Error in '${enum.getClass}' field. Possible values: ${enum.values}")
}
}
class EnumWithIdsFormatter[T <: Enumeration](enum: T) extends Format[T#Value] {
private val nameField = "name"
private val idField = "id"
override def writes(o: T#Value): JsValue = JsObject(Map(idField -> JsNumber(o.id), nameField -> JsString(o.toString)))
override def reads(json: JsValue): JsResult[T#Value] = json match {
case JsObject(values) =>
values.get(idField) match {
case Some(JsNumber(value)) if value <= enum.maxId && value >= 0 ⇒
try {
JsSuccess(enum(value.toInt))
} catch {
case _: NoSuchElementException =>
JsError(
s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$value'")
}
case Some(JsNumber(x)) ⇒
JsError(s"Invalid JSON:$json. Field '$idField': '$x' is out of range. Possible values:${enum.values.map(_.id)}")
case Some(_) ⇒
JsError(s"Invalid JSON:$json. Field '$idField' isn't number. Possible values:${enum.values.map(_.id)}")
case None ⇒
JsError(s"Invalid JSON:$json. Missing field '$idField'")
}
case _ =>
JsError(s"Invalid JSON: $json")
}
}
}
Now I want to validate json
import play.api.libs.json.Json
import play.api.libs.functional.syntax._
val obj = SearchRequestMessage(UUID.randomUUID(), UUID.randomUUID(), "email", SearchByTypes.Emails)
val json = Json.toJson(obj)
val result = ((json \ "jobId").validate[UUID] and
(json \ "offerId").validate[UUID] and
(json \ "search").validate[String] and
(json \ "searchByType").validate[SearchByType])(SearchRequestMessage[String].apply(_,_,_,_))
and an error appear:
Error:(41, 75) missing argument list for method apply in object SearchRequestMessage
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_,_,_,_)` instead of `apply`.
(json \ "searchByType").validate[SearchByType])(SearchRequestMessage[String].apply(_,_,_,_))
How I can fix this error?
try
(json \ "searchByType").validate[SearchByType])(SearchRequestMessage[String].apply)
or (which is a bit verbose)
(json \ "searchByType").validate[SearchByType])((a,b,c,d) => SearchRequestMessage[String].apply(a,b,c,d))
This works
((json \ "jobId").validate[UUID] and
(json \ "offerId").validate[UUID] and
(json \ "search").validate[String] and
(json \ "searchByType").validate[SearchByType])((a: UUID, b: UUID, c: String, d: SearchByType) => SearchRequestMessage.apply[String](a,b,c,d))

convert json to array of scala objects using spray json

I am not much familier with spray json, but I have to convert the below json into Array[myTest]
Below is the code, but it doesnt work. It throws the following errors: How do I fix them?
Error:(19, 54) Cannot find JsonReader or JsonFormat type class for Array[A$A61.this.myTest]
lazy val converted= trainingDataRef.toJson.convertTo[Array[myTest]]
^
Error:(19, 54) not enough arguments for method convertTo: (implicit evidence$1: spray.json.JsonReader[Array[A$A61.this.myTest]])Array[A$A61.this.myTest].
Unspecified value parameter evidence$1.
lazy val converted= trainingDataRef.toJson.convertTo[Array[myTest]]
^
Error:(10, 61) could not find implicit value for evidence parameter of type spray.json.DefaultJsonProtocol.JF[Map[String,Any]]
implicit val format: RootJsonFormat[myTest] = jsonFormat3(myTest.apply)
^
Code: ^
import spray.json.DefaultJsonProtocol._
import spray.json._
case class myTest (
id: String,
classDetails: Map[String, Any],
school: Map[String, Any])
object myTest {
implicit val format: RootJsonFormat[myTest] = jsonFormat3(myTest.apply)
}
val trainingDataRef = """[{"id":"my-id","classDetails":{"sec":"2","teacher":"John"},"school":{"name":"newschool"}}]"""
println(trainingDataRef.getClass)
val converted= trainingDataRef.toJson.convertTo[Array[myTest]]
println(converted)
spray-json has good documentation, try take a look there. Basically, you have to define your case classes and implement JsonFormat for them:
import spray.json.DefaultJsonProtocol._
import spray.json._
case class ClassDetails(sec: String, teacher: String)
object ClassDetails {
implicit val format: RootJsonFormat[ClassDetails] = jsonFormat2(ClassDetails.apply)
}
case class School(name: String)
object School {
implicit val format: RootJsonFormat[School] = jsonFormat1(School.apply)
}
case class ClassInfo
(
id: String,
classDetails: ClassDetails,
school: School
)
object ClassInfo {
implicit object ClassInfoFormat extends RootJsonFormat[ClassInfo] {
def write(c: ClassInfo): JsValue = JsObject(
"id" -> JsString(c.id),
"classDetails" -> c.classDetails.toJson,
"school" -> c.school.toJson
)
def read(value: JsValue): ClassInfo = {
value.asJsObject.getFields("id", "classDetails", "school") match {
case Seq(JsString(name), details, school) =>
new ClassInfo(name, details.convertTo[ClassDetails], school.convertTo[School])
case _ => throw new DeserializationException("ClassInfo expected")
}
}
}
}
val json = """[{"id":"my-id","classDetails":{"sec":"2","teacher":"John"},"school":{"name":"newschool"}}]"""
// JSON string to case classes
val classInfos = json.parseJson.convertTo[Seq[ClassInfo]]
classInfos.zipWithIndex.foreach { case (c, idx) =>
println(s"$idx => $c")
}
println
// Seq[ClassInfo] to JSON
println(s"$classInfos: ")
println(classInfos.toJson.prettyPrint)

Not able to parse Map with Enum to Json in Play Scala

We use Scala 2.11.8 and Play framework 2.5.8
Data to work with can be as simple as that:
object EnumA extends Enumeration {
type EnumA = Value
val ONE, TWO, THREE = Value
}
case class NoWork(data: Map[EnumA.Value, String] = Map.empty)
And what I want to archive is to be able to parse the NoWork class to Json. I know that to it requires providing an implicit formatter for Enumeration.
I have found this solution: https://stackoverflow.com/a/15489179/1549135 and applied it.
The companion object providing those implicits looks as follows:
object NoWork {
implicit val enumAFormat = EnumUtils.enumFormat(EnumA)
implicit val jsonModelFormat = Json.format[NoWork]
}
And it always fails with an error:
error: No implicit format for Map[EnumA.Value,String] available.
implicit val jsonModelFormat = Json.format[NoWork]
^
What is the issue?
I have tested and changing the data type to Map[String, String] allows for serialization. The Enum on its own is serializable too, so now - how to fix the Map with Enum type?
Thanks!
Edit
As Pamu's answer
implicit val writes = new Writes[Map[EnumA.Value, String]] {
override def writes(o: Map[EnumA.Value, String]): JsValue = Json.toJson(o.map { case (a, b) => Json.parse(s"""{${Json.toJson(a)}:${Json.toJson(b)}}""")}.toList)
}
would clearly work for this situation, I would actually need a generic solution for other Map[Enum, T] that I could use in whole application.
Note that it is mandatory for Json keys to be strings.
Following code works
Json.toJson(Map("mon" -> EnumA.MON))
Following code does not work because key for valid Json should always be string. Here the key is EnumA.Value which is not String.
scala> Json.toJson(Map(EnumA.MON -> "mon"))
<console>:19: error: No Json serializer found for type scala.collection.immutable.Map[EnumA.Value,String]. Try to implement an implicit Writes or Format for this type.
Json.toJson(Map(EnumA.MON -> "mon"))
But if you want it work as expected provide a writes
implicit val writes = new Writes[Map[EnumA.Value, String]] {
override def writes(o: Map[EnumA.Value, String]): JsValue = Json.toJson(o.map { case (a, b) => Json.parse(s"""{${Json.toJson(a)}:${Json.toJson(b)}}""")}.toList)
}
now following code works
Json.toJson(Map(EnumA.MON -> "hello"))
You can declare format for EnumA as following
object EnumA extends Enumeration {
val MON = Value("monday")
val TUE = Value("Tuesday")
implicit val format = new Format[EnumA.Value] {
override def writes(o: EnumA.Value): JsValue = Json.toJson(o.toString)
override def reads(json: JsValue): JsResult[EnumA.Value] = json.validate[String].map(EnumA.withName(_))
}
}
Scala REPL output
scala> object EnumA extends Enumeration {
| val MON = Value("monday")
| val TUE = Value("Tuesday")
|
| implicit val format = new Format[EnumA.Value] {
| override def writes(o: EnumA.Value): JsValue = Json.toJson(o.toString)
| override def reads(json: JsValue): JsResult[EnumA.Value] = json.validate[String].map(EnumA.withName(_))
| }
| }
defined object EnumA
scala> Json.toJson(EnumA.MON)
res0: play.api.libs.json.JsValue = "monday"
scala> (Json.parse("""{"a": "monday"}""") \ "a").validate[EnumA.Value]
res7: play.api.libs.json.JsResult[EnumA.Value] = JsSuccess(monday,)
scala> (Json.parse("""{"a": "monday"}""") \ "a").validate[EnumA.Value].get
res10: EnumA.Value = monday
scala> Json.toJson(Map("mon" -> EnumA.MON))
res2: play.api.libs.json.JsValue = {"mon":"monday"}
scala> Json.toJson(Map(EnumA.MON -> "mon"))
<console>:19: error: No Json serializer found for type scala.collection.immutable.Map[EnumA.Value,String]. Try to implement an implicit Writes or Format for this type.
Json.toJson(Map(EnumA.MON -> "mon"))
scala> implicit val writes = new Writes[Map[EnumA.Value, String]] {
| override def writes(o: Map[EnumA.Value, String]): JsValue = Json.toJson(o.map { case (a, b) => Json.parse(s"""{${Json.toJson(a)}:${Json.toJson(b)}}""")}.toList)
| }
writes: play.api.libs.json.Writes[Map[EnumA.Value,String]] = $anon$1#65aebb67
scala> Json.toJson(Map(EnumA.MON -> "hello"))
res2: play.api.libs.json.JsValue = [{"monday":"hello"}]
With colleague we have prepared a generic class that provides JSON serialization for Map[E <: Enum[E], T] type.
The Enum type is always converted to String as it is required for JsObject key. The other parameter is generic and is converted using the implicit format: Format[T]
import play.api.data.validation.ValidationError
import play.api.libs.json._
import scala.util.{Failure, Success, Try}
class MapEnumFormat[E <: Enum[E], T](valueOf: (String => E))(implicit format: Format[T]) extends Format[Map[E, T]] {
override def writes(o: Map[E, T]): JsValue = {
JsObject(o.map { case (a, b) => (a.name, Json.toJson(b)) })
}
override def reads(json: JsValue): JsResult[Map[E, T]] = {
val result = Try(json.as[Map[String, T]].map {
case (key, value) =>
valueOf(key) -> value
})
result match {
case Success(status) =>
JsSuccess(status)
case Failure(th) =>
JsError(ValidationError(s"Error while serializing $json: $th"))
}
}
}

Implicit parameters not found

I'm having some trouble figuring out why compiler complains about not finding an implicit parameter for reads because I'm almost sure that it is in the scope. The error is the following:
Error:(13, 18) No Json deserializer found for type Config. Try to implement an implicit Reads or Format for this type.
test.validate[Config].map {
^
Error:(13, 18) not enough arguments for method validate: (implicit rds: play.api.libs.json.Reads[Config])play.api.libs.json.JsResult[wings.m2m.conf.model.Config].
Unspecified value parameter rds.
test.validate[Config].map {
^
and it happens in the following code:
import play.api.libs.json._
import play.api.libs.json.Reads._
import Config.JsonImplicits._
import scala.util.Try
object Test {
def main(args: Array[String]) {
val test = Json.obj("action" -> Config.Action.nameAcquisitionRequest.toString, "value" -> "hola")
test.validate[Config].map {
t => println(t)
t
}
}
}
/**
* Config companion object
*/
object Config {
type ValueType = String
val ActionKey = "action"
val ValueKey = "value"
object Action extends Enumeration {
type Action = Value
val nameAcquisitionRequest = Value("nameAcquisitionRequest")
val nameAcquisitionReject = Value("nameAcquisitionReject")
val nameAcquisitionAck = Value("nameAcquisitionAck")
val broadcast = Value("broadcast")
}
/**
* Json implicit conversions
*/
object JsonImplicits {
implicit object ConfigReads extends Reads[Config] {
def hTypeCast(action: Config.Action.Value, value: Config.ValueType): Config = {
action match {
case Config.Action.nameAcquisitionRequest => NameAcquisitionRequest(value)
case Config.Action.nameAcquisitionReject => NameAcquisitionReject(value)
case Config.Action.nameAcquisitionAck => NameAcquisitionAck(value)
}
}
override def reads(json: JsValue): JsResult[Config] = json match {
case json: JsObject =>
val action = (json \ ActionKey).as[String]
Try(Config.Action.withName(action)) map {
a =>
val value = (json \ ValueKey).as[String]
JsSuccess(hTypeCast(a, value))
} getOrElse (JsError("Can't convert to Config"))
case _ => JsError("Can't convert to Config")
}
}
implicit object ConfigWrites extends OWrites[Config] {
def jsObjectCreator(action: Config.Action.Value, value: Config.ValueType): JsObject = {
Json.obj(ActionKey -> action.toString, ValueKey -> Json.toJson(value))
}
override def writes(o: Config): JsObject = o match {
case c: NameAcquisitionRequest => jsObjectCreator(Config.Action.nameAcquisitionRequest, c.value)
case c: NameAcquisitionReject => jsObjectCreator(Config.Action.nameAcquisitionReject, c.value)
case c: NameAcquisitionAck => jsObjectCreator(Config.Action.nameAcquisitionAck, c.value)
}
}
}
}
sealed trait Config {
val value: Config.ValueType
}
/**
* Intermediate config message
* #param value
*/
case class NameAcquisitionRequest(override val value: String)
extends Config
case class NameAcquisitionReject(override val value: String)
extends Config
case class NameAcquisitionAck(override val value: String)
extends Config
case class Broadcast(override val value: String)
extends Config
the error occurs when executing the main method on the Test object. To make this example work, make sure to add the following dependency in the SBT: "com.typesafe.play" %% "play-json" % "2.4.1" . And I'm not sure, but maybe this resolver is needed: resolvers += "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases/"
I am not sure what you are trying to achieve and whether this solves your problem but here you go:
test.validate[Config](Config.JsonImplicits.ConfigReads).map {
t => println(t)
t
}

Play Framework / Scala: abstract repository and Json de/serialization

This question is maybe more about Scala than Play, but here it is: I am trying to achieve an abstraction of a repository for common DB operations.
trait Entity {
def id: UUID
}
trait Repository[T <: Entity] {
val JSON_KEY_ID = "_id"
def collection: JSONCollection
def insert(t: T): Future[Either[String, T]] = {
collection.insert(t).map(wr => if (wr.ok) Right(t) else Left(wr.getMessage()))
.recover { case t => Left(t.getMessage) }
}
def update(t: T): Future[Either[String, T]] = {
val selector = Json.obj(JSON_KEY_ID -> t.id.toString)
collection.update(selector, t).map(wr => if (wr.ok) Right(t) else Left(wr.getMessage()))
.recover { case t => Left(t.getMessage) }
}
}
Then I have the objects I would like to use this with:
case class UserDAO(id: UUID) extends Entity[UserDAO]
object UserDAO {
val JSON_KEY_ID = "_id"
implicit val userDaoWrites: OWrites[UserDAO] = new OWrites[UserDAO] {
def writes(user: UserDAO): JsObject = Json.obj(
JSON_KEY_ID -> JsString(user.id.toString)
)
}
implicit val userDaoReads: Reads[UserDAO] = (
(__ \ JSON_KEY_ID).read[UUID]
)(UserDAO.apply _)
}
and then I define its repository like this:
class UserRepository #Inject()(val reactiveMongoApi: ReactiveMongoApi) extends Repository[UserDAO] {
val db = reactiveMongoApi.db
override def collection: JSONCollection = db.collection[JSONCollection]("users")
}
The error I get is
No Json serializer as JsObject found for type T. Try to implement an implicit OWrites or OFormat for this type.
collection.insert(t).map(wr => if (wr.ok) Right(t) else Left(wr.getMessage()))
^
I tried to provide implicit OWrites[T], or even implicit OWrites[_] to no avail. Maybe what I am trying to achieve is impossible. If not, how could I solve this? Thank you very much.
You should be able to just use a context bound.
trait Entity {
def id: UUID
}
class Repository[T <: Entity : Writes] {
...
}
That will ensure that if there exists an implicit Writes[T] in scope, it will be available for your insert and update functions.