For this class
case class AnswerOfAPracticeQuestion (question_id: UUID,
image: Option[List[String]],
notes: Option[String],
answer:List[AnswerSection],
answer_id:Option[UUID],
answeredBy: Option[Answerer],
creationYear:Option[Long],
creationMonth:Option[Long],
){
}
the Reads method fails when I send this json to it
{
"new-answer":{
"images":[
"image 1",
"image 2"
],
"notes":"some notes",
"answer":[
{
"filename":"f1",
"answer":"a1"
},
{
"filename":"f2",
"answer":"a2"
}
],
"answer-id":"11111111-1111-1111-1111-111111111112",
"question-id":"11111111-1111-1111-1111-111111111111",
"creator-id":"11111111-1111-1111-1111-111111111113"
}
}
Reads method is
implicit val answerOfAPracticeQuestionReads: Reads[AnswerOfAPracticeQuestion] = (
(JsPath \ "question-id").read[UUID] and
(JsPath \ "images").readNullable[List[String]] and
(JsPath \ "notes").readNullable[String] and
(JsPath \ "answer").read[List[AnswerSection]] and //FAILS FOR THIS LINE
(JsPath \ "answer-id").readNullable[UUID] and
(JsPath \ "answerer").readNullable[Answerer] and
(JsPath \ "creation-year").readNullable[Long] and //year/month are for internal consumption so not worried about receiving it from a client
(JsPath \ "creation-month").readNullable[Long]
)(AnswerOfAPracticeQuestion.apply _)
Reads for AnswerSection is
implicit val answerSectionReads:Reads[AnswerSection] = (
(JsPath \ "filename").read[String] and
(JsPath \ "answer").read[String]
)(AnswerSection.apply _)
The error is
java.lang.ExceptionInInitializerError
at models.AnswerImplicits$.<init>(Answers.scala:92)
at models.AnswerImplicits$.<clinit>(Answers.scala)
at models.AnswerImplicits$AnswerAPIReads$.reads(Answers.scala:108)
at play.api.libs.json.ConstraintReads.$anonfun$optionWithNull$1(JsConstraints.scala:110)
at play.api.libs.json.Reads$$anon$8.reads(Reads.scala:132)
at play.api.libs.json.JsLookupResult.validateOpt(JsLookup.scala:164)
at play.api.libs.json.JsLookupResult.validateOpt$(JsLookup.scala:162)
at play.api.libs.json.JsDefined.validateOpt(JsLookup.scala:176)
at play.api.libs.json.JsValue.validateOpt(JsValue.scala:20)
at play.api.libs.json.JsValue.validateOpt$(JsValue.scala:19)
at play.api.libs.json.JsObject.validateOpt(JsValue.scala:124)
at controllers.AnswerController.$anonfun$newAnswer$1(AnswerController.scala:399)
at com.mohiva.play.silhouette.api.actions.SecuredActionBuilder.$anonfun$invokeBlock$8(SecuredAction.scala:216)
at com.mohiva.play.silhouette.api.actions.SecuredRequestHandlerBuilder.$anonfun$invokeBlock$2(SecuredAction.scala:93)
at com.mohiva.play.silhouette.api.RequestHandlerBuilder.handleInitializedAuthenticator(RequestHandler.scala:169)
at com.mohiva.play.silhouette.api.RequestHandlerBuilder.handleBlock(RequestHandler.scala:119)
at com.mohiva.play.silhouette.api.RequestHandlerBuilder.handleBlock$(RequestHandler.scala:117)
at com.mohiva.play.silhouette.api.actions.SecuredRequestHandlerBuilder.handleBlock(SecuredAction.scala:52)
at com.mohiva.play.silhouette.api.actions.SecuredRequestHandlerBuilder.$anonfun$invokeBlock$1(SecuredAction.scala:93)
at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:302)
at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:37)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:140)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.pollAndExecAll(ForkJoinPool.java:1021)
at java.util.concurrent.ForkJoinPool$WorkQueue.execLocalTasks(ForkJoinPool.java:1046)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1058)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: scala.UninitializedFieldError: Uninitialized field: C:\Users\manuc\Documents\manu\codingjedi\code_related\code\frontend\web\app\models\Answers.scala: 196
at models.AnswerImplicits$.answerOfAPracticeQuestionWrites(Answers.scala:196)
at models.PracticeQuestionImplicits$.<init>(PracticeQuestion.scala:139)
at models.PracticeQuestionImplicits$.<clinit>(PracticeQuestion.scala)
... 29 more
Futures timed out after [5000 milliseconds]
java.util.concurrent.TimeoutException: Futures timed out after [5000 milliseconds]
...
If I compare the implicit methods, in the pics below, you'll notice that the reads, there is an additional canBuildForm while there is nothing like it in writes
Reads
Writes
Related
I am sending this json to my scala code but I am getting error `` when the json is getting parsed.
{"practice-question":{"title":"dummy title","description":"<p>some D</p><p><strong>with bold</strong></p><ol><li><strong>no 1</strong></li></ol>","tags":["javascript"],"references":["dummy references"],"hints":["<p><u>some hint</u></p>"],"image":["data:image/png;base64,iVBORw0],"answer":[{"filename":"c.js","answer":"function c(){\n c;\n}"}],"success-test":"dummy success test","fail-test":"dummy fail test"}}))
The json validation starts at
val questionOption = jsonBody.validateOpt[Question] //should give JsSuccess or JsError
It should call the following Reads
implicit val questionReads:Reads[Question] = (JsPath \ "practice-question").read[PracticeQuestion](PracticeQuestionReads)
.map((x:PracticeQuestion)=>Question.apply (x))
The Reads for PracticeQuestion is
implicit object PracticeQuestionReads extends Reads[PracticeQuestion] {
def reads(json:JsValue):JsResult[PracticeQuestion] = {
println(s"validating json ${json}")
val structurallyValidated = json.validateOpt[PracticeQuestion](practiceQuestionValidateStructureReads) //check that structurally the json maps to PracticeQuestion
println(s"structurally validated ${structurallyValidated}")
val structuralValidationResult = structurallyValidated match {
case JsSuccess(questionOpt,path)=>{
val result = questionOpt.map(question=>{
//TODOM - should check size of other lists (hints, references etc.)
if(question.image.length <0 || question.image.length > 3)
{
JsError("invalid no. of images")
}
else {
JsSuccess(question)
}
}).getOrElse(JsError("Error in validating Json"))
result
}
case JsError(errors) =>{
//TODOM - replace print with logger.
println("errors in json structure: "+errors)
JsError(errors)
}
}
structuralValidationResult match {
case JsSuccess(question,path)=>{
//TODOM - check that for other mandatory fields (esp in Lists)are not empty
if(question.answer.length == 0){
JsError("Missing Answer")
} else {
JsSuccess(question);
}
}
case JsError(errors) =>{
JsError(errors)
}
}
}
}
and Reads used in PracticeQuestionReads is
implicit val practiceQuestionValidateStructureReads: Reads[PracticeQuestion] = {
println("in conversion");
val p = //p is of type FunctionBuilder[Reads]#CanBuild. This is an intermediary object used to create Reads[PracticeQuestion].
(JsPath \ "question-id").readNullable[UUID] and
(JsPath \ "description").read[String] and
(JsPath \ "hints").read[List[String]] and
(JsPath \ "image").read[List[String]] and //while image data is optional, this field is always present i.e. will get at least image:[]
(JsPath \ "success-test").read[String] and
(JsPath \ "fail-test").read[String] and
(JsPath \ "tags").read[Set[String]] and
(JsPath \ "title").read[String] and
(JsPath \ "answer").read[List[AnswerSection]] and
(JsPath \ "references").read[List[String]] and
(JsPath \ "question-creator").readNullable[QuestionCreator] and
(JsPath \ "creation-year").readNullable[Long] and //year/month are for internal consumption so not worried about receiving it from a client
(JsPath \ "creation-month").readNullable[Long] and
(JsPath \ "creation-hour").readNullable[Long] and
(JsPath \ "creation-minute").readNullable[Long]
//apply method of CanBuildX with a function to translate individual values to your model, this will return your complex Reads
val q = p.apply((PracticeQuestion.apply _))
q
}
To me the structure etc. look ok. What am I doing wrong?
UPDATE, after two months, the error occurred but for a different. I am running a test and am sending a json from the test. The error is
Uninitialized field: C:\Users\manuc\Documents\manu\codingjedi\code_related\code\frontend\web\app\controllers\AnswerController.scala: 34
scala.UninitializedFieldError: Uninitialized field: C:\Users\manuc\Documents\manu\codingjedi\code_related\code\frontend\web\app\controllers\AnswerController.scala: 34
at controllers.AnswerController.logger(AnswerController.scala:34)
at controllers.AnswerController.$anonfun$maxAllowedBodySize$1(AnswerController.scala:33)
at scala.runtime.java8.JFunction0$mcJ$sp.apply(JFunction0$mcJ$sp.java:12)
at scala.Option.getOrElse(Option.scala:121)
at controllers.AnswerController.<init>(AnswerController.scala:33)
at UnitSpecs.ControllerSpecs.AnswerControllerSpecTestEnv.<init>(AnswerControllerSpecTestEnv.scala:98)
at UnitSpecs.ControllerSpecs.AnswerControllerUnitSpec.$anonfun$new$2(AnswerControllerUnitSpec.scala:67)
at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
at org.scalatest.Transformer.apply(Transformer.scala:22)
at org.scalatest.Transformer.apply(Transformer.scala:20)
at org.scalatest.WordSpecLike$$anon$1.apply(WordSpecLike.scala:1078)
at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
at org.scalatest.WordSpec.withFixture(WordSpec.scala:1881)
at org.scalatest.WordSpecLike.invokeWithFixture$1(WordSpecLike.scala:1076)
at org.scalatest.WordSpecLike.$anonfun$runTest$1(WordSpecLike.scala:1088)
at org.scalatest.SuperEngine.runTestImpl(Engine.scala:289)
at org.scalatest.WordSpecLike.runTest(WordSpecLike.scala:1088)
at org.scalatest.WordSpecLike.runTest$(WordSpecLike.scala:1070)
at UnitSpecs.ControllerSpecs.AnswerControllerUnitSpec.org$scalatest$BeforeAndAfterEach$$super$runTest(AnswerControllerUnitSpec.scala:32)
at org.scalatest.BeforeAndAfterEach.runTest(BeforeAndAfterEach.scala:221)
at org.scalatest.BeforeAndAfterEach.runTest$(BeforeAndAfterEach.scala:214)
at UnitSpecs.ControllerSpecs.AnswerControllerUnitSpec.runTest(AnswerControllerUnitSpec.scala:32)
at org.scalatest.WordSpecLike.$anonfun$runTests$1(WordSpecLike.scala:1147)
at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:396)
at scala.collection.immutable.List.foreach(List.scala:389)
at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:384)
at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:373)
at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:410)
at scala.collection.immutable.List.foreach(List.scala:389)
at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:384)
at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:379)
at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:461)
at org.scalatest.WordSpecLike.runTests(WordSpecLike.scala:1147)
at org.scalatest.WordSpecLike.runTests$(WordSpecLike.scala:1146)
at org.scalatest.WordSpec.runTests(WordSpec.scala:1881)
at org.scalatest.Suite.run(Suite.scala:1147)
at org.scalatest.Suite.run$(Suite.scala:1129)
at org.scalatest.WordSpec.org$scalatest$WordSpecLike$$super$run(WordSpec.scala:1881)
at org.scalatest.WordSpecLike.$anonfun$run$1(WordSpecLike.scala:1192)
at org.scalatest.SuperEngine.runImpl(Engine.scala:521)
at org.scalatest.WordSpecLike.run(WordSpecLike.scala:1192)
at org.scalatest.WordSpecLike.run$(WordSpecLike.scala:1190)
at UnitSpecs.ControllerSpecs.AnswerControllerUnitSpec.org$scalatest$BeforeAndAfterAll$$super$run(AnswerControllerUnitSpec.scala:32)
at org.scalatest.BeforeAndAfterAll.liftedTree1$1(BeforeAndAfterAll.scala:213)
at org.scalatest.BeforeAndAfterAll.run(BeforeAndAfterAll.scala:210)
at org.scalatest.BeforeAndAfterAll.run$(BeforeAndAfterAll.scala:208)
at UnitSpecs.ControllerSpecs.AnswerControllerUnitSpec.org$scalatestplus$play$BaseOneAppPerSuite$$super$run(AnswerControllerUnitSpec.scala:32)
at org.scalatestplus.play.BaseOneAppPerSuite.run(BaseOneAppPerSuite.scala:46)
at org.scalatestplus.play.BaseOneAppPerSuite.run$(BaseOneAppPerSuite.scala:41)
at UnitSpecs.ControllerSpecs.AnswerControllerUnitSpec.run(AnswerControllerUnitSpec.scala:32)
at org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:45)
at org.scalatest.tools.Runner$.$anonfun$doRunRunRunDaDoRunRun$13(Runner.scala:1340)
at org.scalatest.tools.Runner$.$anonfun$doRunRunRunDaDoRunRun$13$adapted(Runner.scala:1334)
at scala.collection.immutable.List.foreach(List.scala:389)
at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun(Runner.scala:1334)
at org.scalatest.tools.Runner$.$anonfun$runOptionallyWithPassFailReporter$24(Runner.scala:1031)
at org.scalatest.tools.Runner$.$anonfun$runOptionallyWithPassFailReporter$24$adapted(Runner.scala:1010)
at org.scalatest.tools.Runner$.withClassLoaderAndDispatchReporter(Runner.scala:1500)
at org.scalatest.tools.Runner$.runOptionallyWithPassFailReporter(Runner.scala:1010)
at org.scalatest.tools.Runner$.run(Runner.scala:850)
at org.scalatest.tools.Runner.run(Runner.scala)
at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.runScalaTest2(ScalaTestRunner.java:133)
at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.main(ScalaTestRunner.java:27)
Process finished with exit code 0
The error seem to be for line val logger = LoggerFactory.getLogger(this.getClass.getName) which to be honest doesn't make sense.
In the following code, if I use (), I don't need to explicitly call apply of CanBuilder
implicit val pReads:Reads[P] = (
(JsPath \ "id").readNullable[UUID] and
(JsPath \ "description").read[String] and
...
)(P.apply _)
But if I use {}, I have to call apply. Why?
implicit val pReads:Reads[P] = {
(JsPath \ "id").readNullable[UUID] and
(JsPath \ "description").read[String] and
...
}.apply((P.apply _))
I'm trying to set up a JSON response that I got from a weather API to fit in a model class I defined in order to use it easily, but I can't get to do it.
Here is the class :
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Forecast(var main: String, var description: String, var temp: Int, var tempMin: Int, var tempMax: Int)
object Forecast {
implicit val forecastJsonFormat: Reads[Forecast] = (
(JsPath \ "weather" \\"main").read[String] and
(JsPath \ "weather" \\"description").read[String] and
(JsPath \ "main" \\"temp").read[Int] and
(JsPath \ "main" \\"temp_min").read[Int] and
(JsPath \ "main" \\"temp_max").read[Int]
) (Forecast.apply _)
}
and this is the code in the controller :
def weather = Action.async {
futureResponse.map(response => {
val jsonString = response.json.toString()
val jsonObject = Json.parse(jsonString)
// TODO: Create t [Forecast] Object which represents the response.json data to send it to the view below
Ok(views.html.weather(t))
})}
example of the response.json I'am getting :
{"coord":{"lon":37.62,"lat":55.75},"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13n"},{"id":701,"main":"Mist","description":"mist","icon":"50n"}],"base":"stations","main":{"temp":269.15,"pressure":1024,"humidity":92,"temp_min":268.15,"temp_max":270.15},"visibility":3100,"wind":{"speed":2,"deg":200},"clouds":{"all":90},"dt":1546266600,"sys":{"type":1,"id":9029,"message":0.0029,"country":"RU","sunrise":1546235954,"sunset":1546261585},"id":524901,"name":"Moscow","cod":200}
You have to change main to Seq[String] and description to Seq[String] and temp, tempMin, tempMax to Double
I used a different way to create the reads here, but this way will throw an exception if the format is different than the expected format.
case class Forecast(main: Seq[String], description: Seq[String], temp: Double, tempMin: Double, tempMax: Double)
object Forecast {
val reads = new Reads[Forecast] {
override def reads(json: JsValue): JsResult[Forecast] = {
val main = (json \ "weather" \\ "main").map(_.as[String]).toList
val description = (json \ "weather" \\ "description").map(_.as[String]).toList
val temp = (json \ "main" \ "temp").as[Double]
val tempMin = (json \ "main" \ "temp_min").as[Double]
val tempMax = (json \ "main" \ "temp_max").as[Double]
JsSuccess(Forecast(main, description, temp, tempMin, tempMax))
}
}
}
or you can use the same way you are using, but parse the list in different way:
val forecastJsonFormat: Reads[Forecast] = (
(JsPath \ "weather").read[List[Map[String, JsValue]]].map(_.map(_("main").as[String])) and
(JsPath \ "weather").read[List[Map[String, JsValue]]].map(_.map(_("description").as[String])) and
(JsPath \ "main" \ "temp").read[Double] and
(JsPath \ "main" \ "temp_min").read[Double] and
(JsPath \ "main" \ "temp_max").read[Double]
) (Forecast.apply _)
I finally got to do it and here is how :
In the model I have my case class defined and a companion object that parses the JSON response I got from the web API to my class arguments
Model code :
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Forecast(main: String, description: String, temp: Double, tempMin: Double, tempMax: Double)
object Forecast {
implicit val forecastReads: Reads[Forecast] = (
(JsPath \ "weather" \\ "main").read[String] and
(JsPath \ "weather" \\ "description").read[String] and
(JsPath \ "main" \ "temp").read[Double] and
(JsPath \ "main" \ "temp_min").read[Double] and
(JsPath \ "main" \ "temp_max").read[Double]
) (Forecast.apply _)
}
In the controller code I added a pattern matching and here it is !
Controller code :
def weather = Action.async {
futureResponse.map(response => {
val parseResult = Json.fromJson[Forecast](response.json)
parseResult match {
case JsSuccess(forecast, JsPath) => Ok(views.html.weather(forecast))
case JsError(error) => InternalServerError("Something went wrong!!") // Note that I'm not sure this result exists in Play...
}
})
}
I get the "time" from mysql and need to render it to fe, but I get the "time" in the form of timestamp and I want it displayed as yyyy-MM-dd HH:mm:ss, how can I do it in the server code as follows?
object JDItem {
val jditem = {
get[Long]("id") ~
get[String]("name") ~
get[String]("url") ~
get[Double]("price") ~
get[Int]("commentnum") ~
get[Int]("likerate") ~
get[DateTime]("time") ~
get[String]("category") map {
case id ~ name ~ url ~price~
commentnum~likerate~time~category => JDItem(id,name,url,price,
commentnum,likerate,time,category)
}
}
implicit val JDItemWrites: Writes[JDItem] = (
(JsPath \ "id").write[Long] and
(JsPath \ "name").write[String] and
(JsPath \ "url").write[String] and
(JsPath \ "price").write[Double] and
(JsPath \ "commentnum").write[Int] and
(JsPath \ "likerate").write[Int] and
(JsPath \ "time").write[DateTime] and
(JsPath \ "category").write[String]
)(unlift(JDItem.unapply))
def getJson(category:String,sort:String,DescOrAsc:String):JsObject = DB.withConnection{ implicit c =>
val list = SQL("select id,name,url,price,commentnum,likerate,time,category from "+category+" order by "+sort+" "+DescOrAsc+" limit 100").as(jditem *)
val json:JsValue = Json.toJson(list)
val jsobject = Json.obj("total"-> list.length,"rows"-> json)
jsobject
}
}
Since version 2.3.8 of Anorm, Joda & Java8 temporal types are supported in result parser; see Anorm column: get[DateTime].
I need to serialize a String and an Option[Boolean]:
val myWrites = (
(__ \ "box").write(
(
(__ \ "name").write[String] ~
(__ \ "default").writeNullable[Boolean]
).tupled
)
)
If Option[Boolean] is Some then I'd expect
{
"box": {
"name": "John",
"default": true
}
}
... while if Option[Boolean] is None I'd expect
{
"box": {
"name": "John"
}
}
Given the following variables...
val name = "John"
val default = Some(true)
... how do I pass them to the Writes? I've tried this:
myWrites.writes(name, defaul)
... but it doesn't compile:
No Json serializer found for type play.api.libs.functional.FunctionalBuilder[play.api.libs.json.OWrites]#CanBuild2[String,Option[Boolean]].
Try to implement an implicit Writes or Format for this type.
[error] (__ \ "box").write(
I think its just a typo in your writes. you have defaul vs default
I was able to use
import play.api.libs.json._
import play.api.libs.functional.syntax._
val myWrites = (
(__ \ "box").write(
(
(__ \ "name").write[String] ~
(__ \ "default").writeNullable[Boolean]
).tupled
)
)
myWrites.writes("hi",Some(true))
and I got back
res0: play.api.libs.json.JsObject = {"box":{"name":"hi","default":true}}