Play Framework Strange Behavior when Parsing JSON Body - scala

I have a endpoint that takes in a JSON body. I have implicit reads and writes for this JSON format. In the endpoint, I do a validation of the JSON and fold on the result! Here it is:
def updatePowerPlant(id: Int) = Action.async(parse.tolerantJson) { request =>
request.body.validate[PowerPlantConfig].fold(
errors => {
Future.successful(
BadRequest(
Json.obj("message" -> s"invalid PowerPlantConfig ${errors.mkString(",")}")
).enableCors
)
},
success => {
dbService.insertOrUpdatePowerPlant(success).runAsync.materialize.map {
case Failure(ex) =>
InternalServerError(s"Error updating PowerPlant " +
s"Reason => ${ex.getMessage}").enableCors
case Success(result) =>
result match {
case Left(errorMessage) =>
BadRequest(Json.obj("message" -> s"invalid PowerPlantConfig $errorMessage")).enableCors
case Right(updatedConfig) =>
Ok(Json.prettyPrint(Json.toJson(updatedConfig))).enableCors
}
}
}
)
}
So as it can be seen that I fold on the error and I return a BadRequest. But when I tried writing a unit test, I do not get the HTTP status as BadRequest as I expect, but the test crashes with an exception as below:
JsResultException(errors:List((,List(ValidationError(List(error.expected.jsnumber),WrappedArray())))))
play.api.libs.json.JsResultException: JsResultException(errors:List((,List(ValidationError(List(error.expected.jsnumber),WrappedArray())))))
at play.api.libs.json.JsReadable$$anonfun$2.apply(JsReadable.scala:23)
at play.api.libs.json.JsReadable$$anonfun$2.apply(JsReadable.scala:23)
at play.api.libs.json.JsResult$class.fold(JsResult.scala:73)
at play.api.libs.json.JsError.fold(JsResult.scala:13)
at play.api.libs.json.JsReadable$class.as(JsReadable.scala:21)
at play.api.libs.json.JsDefined.as(JsLookup.scala:132)
at com.inland24.plantsim.models.package$$anon$1.reads(package.scala:61)
at play.api.libs.json.JsValue$class.validate(JsValue.scala:18)
at play.api.libs.json.JsObject.validate(JsValue.scala:76)
at com.inland24.plantsim.controllers.PowerPlantController$$anonfun$updatePowerPlant1$1.apply(PowerPlantController.scala:64)
at com.inland24.plantsim.controllers.PowerPlantController$$anonfun$updatePowerPlant1$1.apply(PowerPlantController.scala:63)
at play.api.mvc.Action$.invokeBlock(Action.scala:498)
at play.api.mvc.Action$.invokeBlock(Action.scala:495)
at play.api.mvc.ActionBuilder$$anon$2.apply(Action.scala:458)
at com.inland24.plantsim.controllers.PowerPlantControllerTest$$anonfun$4$$anonfun$apply$mcV$sp$11.apply(PowerPlantControllerTest.scala:313)
at com.inland24.plantsim.controllers.PowerPlantControllerTest$$anonfun$4$$anonfun$apply$mcV$sp$11.apply(PowerPlantControllerTest.scala:296)
at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
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$class.withFixture(TestSuite.scala:196)
at com.inland24.plantsim.controllers.PowerPlantControllerTest.withFixture(PowerPlantControllerTest.scala:40)
at org.scalatest.WordSpecLike$class.invokeWithFixture$1(WordSpecLike.scala:1075)
at org.scalatest.WordSpecLike$$anonfun$runTest$1.apply(WordSpecLike.scala:1088)
at org.scalatest.WordSpecLike$$anonfun$runTest$1.apply(WordSpecLike.scala:1088)
at org.scalatest.SuperEngine.runTestImpl(Engine.scala:289)
at org.scalatest.WordSpecLike$class.runTest(WordSpecLike.scala:1088)
at com.inland24.plantsim.controllers.PowerPlantControllerTest.runTest(PowerPlantControllerTest.scala:40)
at org.scalatest.WordSpecLike$$anonfun$runTests$1.apply(WordSpecLike.scala:1147)
at org.scalatest.WordSpecLike$$anonfun$runTests$1.apply(WordSpecLike.scala:1147)
at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:396)
at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:384)
at scala.collection.immutable.List.foreach(List.scala:392)
at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:384)
at org.scalatest.SuperEngine.org$scalatest$SuperEngine$$runTestsInBranch(Engine.scala:373)
at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:410)
at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:384)
at scala.collection.immutable.List.foreach(List.scala:392)
at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:384)
at org.scalatest.SuperEngine.org$scalatest$SuperEngine$$runTestsInBranch(Engine.scala:379)
at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:461)
at org.scalatest.WordSpecLike$class.runTests(WordSpecLike.scala:1147)
at com.inland24.plantsim.controllers.PowerPlantControllerTest.runTests(PowerPlantControllerTest.scala:40)
at org.scalatest.Suite$class.run(Suite.scala:1147)
at com.inland24.plantsim.controllers.PowerPlantControllerTest.org$scalatest$WordSpecLike$$super$run(PowerPlantControllerTest.scala:40)
at org.scalatest.WordSpecLike$$anonfun$run$1.apply(WordSpecLike.scala:1192)
at org.scalatest.WordSpecLike$$anonfun$run$1.apply(WordSpecLike.scala:1192)
at org.scalatest.SuperEngine.runImpl(Engine.scala:521)
at org.scalatest.WordSpecLike$class.run(WordSpecLike.scala:1192)
at com.inland24.plantsim.controllers.PowerPlantControllerTest.org$scalatest$BeforeAndAfterAll$$super$run(PowerPlantControllerTest.scala:40)
at org.scalatest.BeforeAndAfterAll$class.liftedTree1$1(BeforeAndAfterAll.scala:213)
at org.scalatest.BeforeAndAfterAll$class.run(BeforeAndAfterAll.scala:210)
at com.inland24.plantsim.controllers.PowerPlantControllerTest.run(PowerPlantControllerTest.scala:40)
at org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:45)
at org.scalatest.tools.Runner$$anonfun$doRunRunRunDaDoRunRun$1.apply(Runner.scala:1340)
at org.scalatest.tools.Runner$$anonfun$doRunRunRunDaDoRunRun$1.apply(Runner.scala:1334)
at scala.collection.immutable.List.foreach(List.scala:392)
at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun(Runner.scala:1334)
at org.scalatest.tools.Runner$$anonfun$runOptionallyWithPassFailReporter$2.apply(Runner.scala:1011)
at org.scalatest.tools.Runner$$anonfun$runOptionallyWithPassFailReporter$2.apply(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:138)
at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.main(ScalaTestRunner.java:28)
Here is my unit test:
"not update for an invalid PowerPlantConfig JSON" in {
// We are updating the PowerPlant with id = 101, Notice that the powerPlantId is invalid
val jsBody =
"""
|{
| "powerPlantId":"invalidId",
| "powerPlantName":"joesan 1",
| "minPower":100,
| "maxPower":800,
| "rampPowerRate":20.0,
| "rampRateInSeconds":"2 seconds",
| "powerPlantType":"RampUpType"
|}
""".stripMargin
val result: Future[Result] =
controller.updatePowerPlant(101)
.apply(
FakeRequest().withBody(Json.parse(jsBody))
)
result.materialize.map {
case Success(succ) =>
assert(succ.header.status === BAD_REQUEST)
case Failure(_) =>
fail("Unexpected test failure when Updating a PowerPlant! Please Analyze!")
}
}
Any idea why I'm not getting the expected behavior? I'm expecting that I get a HTTP BadRequest back!
EDIT: To get rid of the unexpected exception, I had to wrap my code into a Try block and I do not want that. So this piece of code gets rid of the error:
def updatePowerPlant(id: Int) = Action.async(parse.tolerantJson) { request =>
scala.util.Try(request.body.validate[PowerPlantConfig]) match {
case Failure(fail) =>
Future.successful(InternalServerError(s"Error updating PowerPlant " +
s"Reason => ${fail.getMessage}").enableCors)
case Success(succ) =>
succ.fold(
errors => {
Future.successful(
BadRequest(
Json.obj("message" -> s"invalid PowerPlantConfig ${errors.mkString(",")}")
).enableCors
)
},
success => {
dbService.insertOrUpdatePowerPlant(success).runAsync.materialize.map {
case Failure(ex) =>
InternalServerError(s"Error updating PowerPlant " +
s"Reason => ${ex.getMessage}").enableCors
case Success(result) =>
result match {
case Left(errorMessage) =>
BadRequest(Json.obj("message" -> s"invalid PowerPlantConfig $errorMessage")).enableCors
case Right(updatedConfig) =>
Ok(Json.prettyPrint(Json.toJson(updatedConfig))).enableCors
}
}
}
)
}
}
But as it can be seen that there is this additional Try(....) block and I do not want this!
Here is my definition of PowerPlantConfig:
sealed trait PowerPlantConfig {
def id: Int
def name: String
def minPower: Double
def maxPower: Double
def powerPlantType: PowerPlantType
}
object PowerPlantConfig {
case class OnOffTypeConfig(
id: Int,
name: String,
minPower: Double,
maxPower: Double,
powerPlantType: PowerPlantType
) extends PowerPlantConfig
case class RampUpTypeConfig(
id: Int,
name: String,
minPower: Double,
maxPower: Double,
rampPowerRate: Double,
rampRateInSeconds: FiniteDuration,
powerPlantType: PowerPlantType
) extends PowerPlantConfig
case class UnknownConfig(
id: Int = -1,
name: String,
minPower: Double,
maxPower: Double,
powerPlantType: PowerPlantType
) extends PowerPlantConfig
// represents all the PowerPlant's from the database
case class PowerPlantsConfig(
snapshotDateTime: DateTime,
powerPlantConfigSeq: Seq[PowerPlantConfig]
)
}
Here is my JSON reads and writes:
implicit val powerPlantCfgFormat: Format[PowerPlantConfig] = new Format[PowerPlantConfig] {
def reads(json: JsValue): JsResult[PowerPlantConfig] = {
val powerPlantTyp = PowerPlantType.fromString((json \ "powerPlantType").as[String])
powerPlantTyp match {
case PowerPlantType.OnOffType =>
JsSuccess(OnOffTypeConfig(
id = (json \ "powerPlantId").as[Int],
name = (json \ "powerPlantName").as[String],
minPower = (json \ "minPower").as[Double],
maxPower = (json \ "maxPower").as[Double],
powerPlantType = powerPlantTyp
))
case PowerPlantType.RampUpType =>
JsSuccess(RampUpTypeConfig(
id = (json \ "powerPlantId").as[Int],
name = (json \ "powerPlantName").as[String],
minPower = (json \ "minPower").as[Double],
rampPowerRate = (json \ "rampPowerRate").as[Double],
rampRateInSeconds = Duration.apply((json \ "rampRateInSeconds").as[String]).asInstanceOf[FiniteDuration],
maxPower = (json \ "maxPower").as[Double],
powerPlantType = powerPlantTyp
))
case _ =>
JsSuccess(UnknownConfig(
id = (json \ "powerPlantId").as[Int],
name = (json \ "powerPlantName").as[String],
minPower = (json \ "minPower").as[Double],
maxPower = (json \ "maxPower").as[Double],
powerPlantType = powerPlantTyp
))
}
}
def writes(o: PowerPlantConfig): JsValue = {
if (o.powerPlantType == RampUpType) {
Json.obj(
"powerPlantId" -> o.id,
"powerPlantName" -> o.name,
"minPower" -> o.minPower,
"maxPower" -> o.maxPower,
"rampPowerRate" -> o.asInstanceOf[RampUpTypeConfig].rampPowerRate,
"rampRateInSeconds" -> o.asInstanceOf[RampUpTypeConfig].rampRateInSeconds.toString(),
"powerPlantType" -> PowerPlantType.toString(o.powerPlantType)
)
}
else {
Json.obj(
"powerPlantId" -> o.id,
"powerPlantName" -> o.name,
"minPower" -> o.minPower,
"maxPower" -> o.maxPower,
"powerPlantType" -> PowerPlantType.toString(o.powerPlantType)
)
}
}
}

According your stacktrace (line I marked)
JsResultException(errors:List((,List(ValidationError(List(error.expected.jsnumber),WrappedArray())))))
play.api.libs.json.JsResultException: JsResultException(errors:List((,List(ValidationError(List(error.expected.jsnumber),WrappedArray())))))
at play.api.libs.json.JsReadable$$anonfun$2.apply(JsReadable.scala:23)
at play.api.libs.json.JsReadable$$anonfun$2.apply(JsReadable.scala:23)
at play.api.libs.json.JsResult$class.fold(JsResult.scala:73)
at play.api.libs.json.JsError.fold(JsResult.scala:13)
--> at play.api.libs.json.JsReadable$class.as(JsReadable.scala:21)
at play.api.libs.json.JsDefined.as(JsLookup.scala:132)
at com.inland24.plantsim.models.package$$anon$1.reads(package.scala:61)
at play.api.libs.json.JsValue$class.validate(JsValue.scala:18)
at play.api.libs.json.JsObject.validate(JsValue.scala:76)
at com.inland24.plantsim.controllers.PowerPlantController$$anonfun$updatePowerPlant1$1.apply(PowerPlantController.scala:64)
at com.inland24.plantsim.controllers.PowerPlantController$$anonfun$updatePowerPlant1$1.apply(PowerPlantController.scala:63)
you used as[Int] in your Read format for id field for PowerPlantConfig.
When you call as[Int], you are trying to force the given json path to type Int. It throws an exception if it cannot (as in your test). You can read on difference betweem as, asOpt and validate here for example
Update
If you look into implementation of as, asOpt and validate you will see that all these three do at first the same thing, but than differs in a way:
validate - I do need result either result or info on failure wrapped (just call reads of implicit arg on json)
asOpt - I need either result or none, if reads under used for resolution return parse error it is ignored as not set at all
as - I need either result, or exception. In other words "I'm sure, that this is always such type, if not, than it is general error"
Both as and asOpt are "extended validate" with interpreting result.
Example
Example how to move from as to validate in hierarchy (two Formats - one as yours with as which will throw exception, and another with validate which will not throw exception):
sealed trait PowerPlantConfig {
def id: Int
}
case class RampUpTypeConfig(id: Int) extends PowerPlantConfig
implicit val powerPlantCfgFormat: Format[PowerPlantConfig] = new Format[PowerPlantConfig] {
def reads(json: JsValue): JsResult[PowerPlantConfig] = {
JsSuccess(RampUpTypeConfig(
id = (json \ "powerPlantId").as[Int]
))
}
def writes(o: PowerPlantConfig): JsValue = {
Json.obj(
"powerPlantId" -> o.id)
}
}
val powerPlantCfgFormatFixed: Format[PowerPlantConfig] = new Format[PowerPlantConfig] {
def reads(json: JsValue): JsResult[PowerPlantConfig] = {
for {
id <- (json \ "powerPlantId").validate[Int]
} yield {
RampUpTypeConfig(
id = id
)
}
}
def writes(o: PowerPlantConfig): JsValue = {
Json.obj(
"id" -> o.id)
}
}
Json.parse("""{"powerPlantId":"123"}""").validate[PowerPlantConfig](powerPlantCfgFormatFixed)
And output will be not an exception but JsFailure as expected
res1: play.api.libs.json.JsResult[PowerPlantConfig] = JsError(List((,List(ValidationError(error.expected.jsnumber,WrappedArray())))))

Related

Json response In scala slick

I am executing Store Procedure In Scala controller class but not able to get result In proper json Format
val dbConfig = Database.forURL("jdbc:mysql://localhost:3306/equineapp?user=root&password=123456", driver = "com.mysql.jdbc.Driver")
val setup1 = sql"call HorsePrfile ($HorseId);".as[(Int,String)]
val res = Await.result(dbConfig.run(setup1), 1000 seconds)
// val json = Json.toJson(res)
Ok(Json.toJson(res.toList))
can anyone tell me how to return json response from above code with header also
this is my model class
case class HorseProfile(HorseID: Int,HorsePicUrl:String)
case class HorseProfileData(HorseID: Int, HorsePicUrl:String)
object HorseForm1 {
val form = Form(
mapping(
"HorseID" -> number,
"HorsePicUrl" ->nonEmptyText ,
)(HorseProfileData.apply)(HorseProfileData.unapply)
)
implicit val fooWrites: Writes[HorseProfile] = (
(__ \ 'HorseID).write[Int] and (__ \ 'HorsePicUrl).write[String]
)(foo => (foo.HorseID, foo.HorsePicUrl))
// val horsepics = TableQuery[HorseProfilePicDef]
}
//
//
class HorseProfilePicDef(tag: Tag) extends Table[HorseProfile](tag, "horse_profile_image") {
def HorseID1 = column[Int]("horseid")
def HorsePicUrl = column[String]("horseimage")
//def HorsePics = column[Blob]("horseimage")
// def * = (HorseID, HorsePics)
override def * =
(HorseID1, HorsePicUrl) <>(HorseProfile.tupled, HorseProfile.unapply)
}
object HorseProfilePics {
val dbConfig = Database.forURL("jdbc:mysql://localhost:3306/equineapp?user=root&password=123456", driver = "com.mysql.jdbc.Driver")
//val dbConfig1 = dbConfig.get[JdbcProfile](Play.current)
val horsepics = TableQuery[HorseProfilePicDef]
implicit val HorseProfile: Writes[HorseProfile] = Json.writes[HorseProfile]
implicit val userJsonFormat = Json.format[HorseProfile]
// val itemWrites: OWrites[(HorseProfile)] = (
// (__ \ "flight").write[HorseProfile]
//// (__ \ "scheduledFlight").write[ScheduledFlight] and
//// (__ \ "airline").write[Airline] and
//// (__ \ "airport1").write[Airport] and
//// (__ \ "airport2").write[Airport]
// ).tupled
implicit val testWriter: OWrites[HorseProfile] = Json.writes[HorseProfile]
//val resultWrites: Writes[Seq[( HorseProfile )]] = Writes.seq(HorseProfile)
implicit def seqWrites[T](implicit fmt: Writes[T]): Writes[Seq[T]] = new Writes[Seq[T]] {
def writes(ts: Seq[T]) = JsArray(ts.toList.map(t => toJson(t)(fmt)))
}
// def HorseImage(HorseId : Int): Future[String] = {
//
//
// val setup1 = sql"call HorsePrfile ($HorseId);".as[(Int, String)]
//
//
// //val res = Await.result(dbConfig.run(setup1), 1000 seconds)
// dbConfig.run(setup1).map(seqWrites =>seqWrites.toString).recover {
// case ex: Exception => ex.getCause.getMessage
//
//
// }
// }
// def writes(tweet: HorseProfile): JsValue = {
// // tweetSeq == Seq[(String, play.api.libs.json.JsString)]
// val tweetSeq = Seq(
// "username" -> JsString(tweet.HorsePicUrl)
//
// )
// JsObject(tweetSeq)
// }
// implicit val locationWrites: Writes[HorseProfile] = (
// (JsPath \ "HorseID").write[Int] and
// (JsPath \ "HorsePicUrl").write[String]
// )(unlift(HorseProfile.))
val resultWrites: Writes[Seq[(HorseProfile)]] = Writes.seq(testWriter)
// def HorseImage( HorseId : Int): Future[String]= {
// val a1=horsepics.filter(i => i.HorseID === HorseId)
// dbConfig.run(horsepics.filter(_.HorseID === HorseId).result.headOption).map(results =>results.toString)
//
// // dbConfig.run(horsepics.filter(_.HorseID === HorseId).result.headOption
//
// }
// def addnew(HorseId:Int,Horsepicurl:String):Future[String]=
// {
//
// val project = HorseProfile(HorseId,Horsepicurl)
// dbConfig.run(HorseProfilePics+=project).map(res => "User successfully added").recover {
// case ex: Exception => ex.getCause.getMessage
// }
//
// //dbConfig.run(users.insertOrUpdate(project))
// }
def gethorseimage(HorseId : Int):Future[Seq[HorseProfile]] = {
// dbConfig.run(horsepics.filter(i => i.HorseID === HorseId ).result.headOption)
val set=horsepics.filter(_.HorseID1 === HorseId )
dbConfig.run(set.result)
}
//
I have declared column here as string but in my database I have declared this column as BLOB and trying to insert data and retrive data as Json format
First of all never ever use Await.result in your actual implementation. What is the point of using Slick, if you don't want to use its concurrent abilities?
Second, you need to map the result, something like:
dbCall.map{
returnedData =>
??? //The following step 3 and four here
}.recover{
//In case something went wrong with your DB call.
case e => InternalServerError("Db failure")
}
Third, you need to represent your returnedData as a case class.
Fourth you use the Json writer to turn a case class into its Json representation:
implicit val someVal: OWrites[SomeCaseClass] = Json.writes[SomeCaseClass]
Json.toJson(someVal)
Update
So based on your comment, I implemented it, such that it uses your return type and turns it into Json. Here it is:
import play.api.libs.json.{Json, OWrites}
case class HorseProfile(i: Int, value: String)
val dbResult: Option[HorseProfile] = Some(HorseProfile(77,"iVBORw0KGgoAAAANSUhE"))
implicit val horseProfileWrites: OWrites[HorseProfile] = Json.writes[HorseProfile]
Json.toJson(dbResult)
And the result I'm getting is this:
res0: play.api.libs.json.JsValue = {"i":77,"value":"iVBORw0KGgoAAAANSUhE"}

Parsing scala Json into dataframe

Sample Json
"alternateId": [
{
"type": "POPID",
"value": "1-7842-0759-001"
},
{
"type": "CAMID",
"value": "CAMID 0000-0002-7EC1-02FF-O-0000-0000-2"
},
{
"type": "ProgrammeUuid",
"value": "1ddb01e2-6146-4e10-bba9-dde40d0ad886"
}
]
I want to update a existing dataframe with two columns, those two columns are POPID and CAMID . These two values needs to be parsed from json structure
I dont know how to parse this structure , Can you help me on what do i need to change on fetchField method. As per above json POPID is placed first and CAMID is placed second, but in real jsons, it can be placed at one of those 3 places inside alternateId.
val fetchCAMID_udf = udf(fetchCAMID _)
val fetchPOPID_udf = udf(fetchPOPID _)
var updatedDf = //Data frame initialize
updatedDf = updatedDf.withColumn("CAMID", fetchCAMID_udf(col("alternate_id")))
updatedDf = updatedDf.withColumn("POPID", fetchPOPID_udf(col("alternate_id")))
updatedDf .show(10,false)
def fetchCAMID(jsonStr: String): String = {
var CAMID: String = fetchField(jsonStr, "CAMID")
CAMID
}
def fetchPOPID(jsonStr: String): String = {
fetchField(jsonStr, "POPID")
}
def fetchField(jsonStr: String, fieldName: String): String = {
try {
implicit val formats = DefaultFormats
val extractedField = jsonStr match {
case "(unknown)" => jsonStr
case _ => {
val json = JsonMethods.parse(jsonStr)
val resultExtracted = (json \\ fieldName)
val result = resultExtracted match {
case _: JString => resultExtracted.extract[String]
case _: JInt => resultExtracted.extract[Int].toString
case _: JObject => "(unknown)"
}
result
}
}
extractedField
}
catch{
case e: Exception =>{
log.error(s"Fetch field failed. Field name: $fieldName . Json: $jsonStr")
"(unknown)"
}
}
}
Change your fetchField function as the following
def fetchField(jsonStr: String, fieldName: String): String = {
try {
val typeAndValue = (JsonMethods.parse("{"+jsonStr+"}") \ "alternateId" \ "type" \\ classOf[JString]).zip(JsonMethods.parse("{"+jsonStr+"}") \ "alternateId" \ "value" \\ classOf[JString])
typeAndValue.filter(_._1 == fieldName).map(_._2).toList(0)
}catch{
case e: Exception =>{
"(unknown)"
}
}
}
and you get the CAMID and POPID populated
you can read the JSON using Spark and get it using regular spark operations
val df=spark.read.option("multiLine",true).json("test.json")
df.select($"alternateId".getItem(0).as("pop"),$"alternateId".getItem(1).as("cam")).select($"pop.value".as("POPID"),$"cam.value".as("CAMID")).show()
+---------------+--------------------+
| POPID| CAMID|
+---------------+--------------------+
|1-7842-0759-001|CAMID 0000-0002-7...|
+---------------+--------------------+

change data type sequence String to String

my DAL :
def login(username: String,pass :String) =
sql"""SELECT AD1SYS.PKG_STC_LOGIN.USER_LOGIN ('#$username','#$pass')FROM DUAL""".as[String]
def getEmpName(username: String,empName :String) =
sql"""SELECT AD1SYS.PKG_STC_LOGIN.GET_EMPL_NAME ('#$username','#$empName') FROM DUAL""".as[(String)]
my service :
def login(username:String,pass:String): Future[Seq[(String)]] = {
db.run(DalLogin.login(username, pass))
}
def getEmpName(username:String,empName:String): Future[Seq[(String)]] = {
db.run(DalLogin.getEmpName(username, empName))
}
My Controller :
def login = Action.async(BodyParsers.parse.json) { request =>
val username = (request.body \ "username").as[String]
val pass = (request.body \ "pass").as[String]
ServiceLogin.login(username, pass).map { data =>
ServiceLogin.getEmpName(username,data).map(result =>
Ok(Json.toJson(result.map( p =>
ModelKodeCabang(p)
))
))}
}
the result from Service.login is Seq(String) "data", i can't use the Seq(String) for parameter ServiceLogin.getEmpName(username,data).. how to convert Seq(String) to String ??

Play framework - Using anorm with Option[LocalDate] \ Option[LocalDateTime]

I am trying to define a nullable date field in postgres, while using anorm as connection to the database.
I am trying to update an entry:
def update(id: Long, startTime: Option[LocalDate]){
SQL("""UPDATE my_table
|SET start_date = {start_date}
|WHERE id = {id}
""".stripMargin)
.on(
'id ->id,
'start_date -> startDate,
).executeUpdate()
}
But I get a compilation error, looks like anorm can't handle Option[DateTime], although when I configured a parser it works form me:
val parser: RowParser[Info] = {
get[Long]("id") ~
get[Option[DateTime]]("start_date") map {
case id ~ startTime => Info(id, startDate)
}
}
What am I missing here?
Thanks!
I added my own implicit definitions:
implicit def rowToLocalDate: Column[LocalDate] = Column.nonNull {(value, meta) =>
val MetaDataItem(qualified, nullable, clazz) = meta
value match {
case ts: java.sql.Timestamp => Right(new LocalDate(ts.getTime))
case d: java.sql.Date => Right(new LocalDate(d.getTime))
case str: java.lang.String => Right(fmt.parseLocalDate(str))
case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass) )
}
}
implicit val localDateToStatement = new ToStatement[LocalDate] {
def set(s: java.sql.PreparedStatement, index: Int, aValue: LocalDate): Unit = {
s.setTimestamp(index, new java.sql.Timestamp(aValue.toDateTimeAtStartOfDay().getMillis()))
}
}
And the relevant ParameterMetaData
implicit object LocalDateClassMetaData extends ParameterMetaData[LocalDate] {
val sqlType = ParameterMetaData.DateParameterMetaData.sqlType
val jdbcType = ParameterMetaData.DateParameterMetaData.jdbcType
}
That made the trick
Related question, Anorm compare/search by java.time LocalDateTime what it worked for me is just update to new version (not-yet-release-one)

How to resolve error "play - Cannot invoke the action, eventually got an error: java.lang.IllegalArgumentException: can't serialize class" in scala?

I am using scala case classes and play framework to process some data.
Here is a my code:
case class myClass(
datastores: Option[List[DataStores]])
case class DataStores(
name: Option[String],
utilization: Option[DataStoreUtilization],
luns: Option[List[DataStoreLuns]]
)
case class DataStoreUtilization(
freeBytes: Option[Long],
sizeBytes: Option[Long],
usedBytes: Option[Long])
case class DataStoreLuns(
model: Option[String],
canonicalName: Option[String],
vendor: Option[String])
object myClass extends ModelCompanion[myClass, ObjectId] {
val collection = MongoClient("mongoserver", 27017)("myDB")("myCollection")
val dao = new SalatDAO[myClass, ObjectId](collection = collection) {}
def apply(rawData: JsValue): myClass = {
val datastoreList = getDataStoreList(rawData)
myClass(datastoreList)
}
private def getDataStoreList(rawData: JsValue): Option[List[DataStores]] = {
val commandInfo = (rawData \ "Commands").as[JsValue]
(commandInfo \ "dataStores").asOpt[List[JsObject]].map { dataStores =>
dataStores map {
dataStoreJson =>
val name = (dataStoreJson \ "name").asOpt[String]
val utilization = getDataStoreUtilization(dataStoreJson)
val luns = getDataStoreLuns(dataStoreJson)
// val virtualMachines = getDataStoreVirtualMachines(dataStoreJson)
DataStores(name, utilization, luns)
}
}
}
private def getDataStoreUtilization(dataStoreJson: JsObject): Option[DataStoreUtilization] = {
(dataStoreJson \ "utilization").asOpt[JsObject].map {
case utilizationJson =>
val freeBytes = (utilizationJson \ "freeBytes").asOpt[Long]
val sizeBytes = (utilizationJson \ "sizeBytes").asOpt[Long]
val usedBytes = (utilizationJson \ "usedBytes").asOpt[Long]
DataStoreUtilization(freeBytes, sizeBytes, usedBytes)
}
}
private def getDataStoreLuns(dataStoreJson: JsValue): Option[List[DataStoreLuns]] = {
(dataStoreJson \ "luns").asOpt[List[JsObject]].map { luns =>
luns map {
lunJson =>
val model = (lunJson \ "model").asOpt[String]
val canonicalName = (lunJson \ "CanonicalName").asOpt[String]
val vendor = (lunJson \ "vendor").asOpt[String]
DataStoreLuns(model, canonicalName, vendor)
}
}
}
}
I am getting following error:
play - Cannot invoke the action, eventually got an error: java.lang.IllegalArgumentException: can't serialize class ....models.DataStoreUtilization
How to resolve this error??