Do not know how to convert JArray(List(JString(dds3), JString(sdds))) into class java.lang.String - scala

~ pathPrefix("system") {
post {
entity(as[JValue]) { system =>
val newPerms = for {
sitePerms <- findAllPermissions((system \ "siteId").extract[String])
} yield {
sitePerms.groupBy(_.userId).mapValues(_.map(_.permissionType).toSet)
}.flatMap { case (userId, perms) =>
val systemId = (system \ "id").extract[String]
perms.map(Permission(userId, systemId, _, "system"))
}
onComplete(newPerms.flatMap(addPermissions)) {
case Success(_) => complete(StatusCodes.NoContent)
case Failure(error) => failWith(error)
}
}
Request Body
[{
"name": "dds3",
"description": "",
"siteId": "abs",
"companyId": "local"
},
{
"name": "dds3",
"description": "",
"siteId": "abc",
"companyId": "local"
}]
Error:
The request content was malformed:
No usable value for name
Do not know how to convert JArray(List(JString(dds3), JString(sdds))) into class java.lang.String
I want to pass a List of array from request body but don't know how to do that in scala, can anyone please help me on that.

The easiest option is to let the entity directive unpick your data for you:
case class System(
name: String,
description: String,
siteId: String,
companyId: String,
)
entity(as[List[System]]) { system =>
system will contain a parsed list of System objects that can be processed in the usual way.

Related

Representing a product to store in mongo using ReactiveMongo library

I am trying to model a product for my mongodb collection "products".
So it looks like this:
{
"_id": "abc123",
"sku": "sku123",
"name": "some product name",
"description": "this is a description",
"specifications": {
"name" : "name1",
"metadata": [
{"name1": "value1"},
{"name2": "value2"},
]
}
}
So my case classes would look like:
case class Product(
id: String,
sku: String,
name: String,
description: String,
specifications: Specification
)
case class Specification(
name: String,
metadata: Metadata
)
case class Metadata(
kvp: Map[String, String]
)
So now I will have to create handlers for each type Product, Specification and Metadata so when data is read/written to mongo it will perform the correct data mapping?
How will I map the Metadata case class, a little confused?
As indicated in the documentation, the Reader/Writer can be simply generated most of the time.
import reactivemongo.api.bson._
// Add in Metadata companion object to be in default implicit scope
implicit val metadataHandler: BSONDocumentHandler[Metadata] = Macros.handler
// Add in Specification companion object to be in default implicit scope
implicit val specHandler: BSONDocumentHandler[Specification] = Macros.handler
// Add in Product companion object to be in default implicit scope
implicit val productHandler: BSONDocumentHandler[Product] = Macros.handler
Then any function using the BSON Reader/Writer typeclasses will accept Product/Specification/Metadata:
BSON.writeDocument(Product(
id = "X",
sku = "Y",
name = "Z",
description = "...",
specifications = Specification(
name = "Lorem",
metadata = Metadata(Map("foo" -> "bar"))))).foreach { doc: BSONDocument =>
println(BSONDocument pretty doc)
}
/* {
'id': 'X',
'sku': 'Y',
'name': 'Z',
'description': '...',
'specifications': {
'name': 'Lorem',
'metadata': {
'kvp': {
'foo': 'bar'
}
}
}
} */

Scala JSON If key matches value return string

I have the JSon response as given below.
If metadata's Organic=true then label='true-Organic', else label='non-Organic'
in the end => return List or Map[modelId,label]
import net.liftweb.json.{DefaultFormats, _}
object test1 extends App {
val json_response =
"""{
"requestId": "91ee60d5f1b45e#316",
"error": null,
"errorMessages": [
],
"entries": [
{
"modelId":"RT001",
"sku": "SKU-ASC001",
"store": "New Jersey",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-01-22T01:25Z",
"organic": "true"
}
},
{
"modelId":"RT002",
"sku": "SKU-ASC002",
"store": "livingstone",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-10-03T01:25Z",
"organic": "false"
}
}
] }"""
tried like this :
val json = parse(json_response)
implicit val formats = DefaultFormats
var map = Map[String, String]()
case class Sales(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata:
Map[String, String])
case class Response(entries: List[Sales])
val response = json.extract[Response]
After this, not sure how to proceed.
This is a straightforward map operation on the entries field:
response.entries.map{ e =>
e.modelId ->
if (e.metadata.get("organic").contains("true")) {
"true-Organic"
} else {
"non-Organic"
}
}
This will return List[(String, String)], but you can call toMap to turn this into a Map if required.

How to decode Date nested in an array in circe?

How would you decode the Date field from a JSON Object nested in a JSON Array?
Sample JSON:
[
{
"msys": {
"relay_message": {
"content": {
"headers": [
{
"Date": "Mon, 4 Jul 2016 15:53:14 +0100"
},
{
"Message-ID": "<484810298443-112311-xqxbby#mail.there.com>"
}
],
"html": "<p>Hi there <strong>SparkPostians</strong>.</p>",
"subject": "We come in peace",
"text": "Hi there SparkPostians.",
"to": [
"your#yourdomain.com"
]
},
"msg_from": "me#here.com",
}
}
}
]
Having trouble accessing the Date since it is nested within the Headers array. Anyone have any ideas?
case class InboundEmail(recipients: Recipients, from: String, content: Content)
case class Recipients(to: List[String], cc: Option[List[String]])
case class Content(subject: String, body: String)
object InboundEmail {
implicit val decoder: Decoder[InboundEmail] = (c: HCursor) ⇒
for {
toList <- c.downArray.downField("msys").downField("relay_message").downField("content").downField("to").as[List[String]]
ccList <- c.downArray.downField("msys").downField("relay_message").downField("content").downField("cc").as[Option[List[String]]]
from <- c.downArray.downField("msys").downField("relay_message").downField("msg_from").as[String]
subject <- c.downArray.downField("msys").downField("relay_message").downField("content").downField("subject").as[String]
body <- c.downArray.downField("msys").downField("relay_message").downField("content").downField("html").as[String]
} yield {
new InboundEmail(Recipients(toList, ccList), from, Content(subject: String, body: String))
}
}
You could add the following to your for-comprehension to extract date as Option[String]
date <- c.downArray.downField("msys").downField("relay_message").downField("content").downField("headers").as[List[Map[String, String]]].map(_.find(_.contains("Date")).flatMap(_.get("Date")))

Create a json deserializer and use it

How do you create a jackson custom serializer and use it in your program? The serializer is used to serialize data from a kafka stream, because my job fails if it encounters a null.
I tried the following to create a serializer.
import org.json4s._
import org.json4s.jackson.JsonMethods._
case class Person(
val user: Option[String]
)
object PersonSerializer extends CustomSerializer[Person](formats => ( {
case JObject(JField("user", JString(user)) :: Nil) => Person(Some(user))
case JObject(JField("user", null) :: Nil) => Person(None)
},
{
case Person(Some(user)) => JObject(JField("user", JString(user)) :: Nil)
case Person(None) => JObject(JField("user", JString(null)) :: Nil)
}))
I am trying to use it this way.
object ConvertJsonTOASTDeSerializer extends App
{
case class Address(street : String, city : String)
case class PersonAddress(name : String, address : Address)
val testJson1 =
"""
{ "user": null,
"address": {
"street": "Bulevard",
"city": "Helsinki",
"country": {
"code": "CD" }
},
"children": [
{
"name": "Mary",
"age": 5,
"birthdate": "2004-09-04T18:06:22Z"
},
{
"name": "Mazy",
"age": 3
}
]
}
"""
implicit var formats : Formats = DefaultFormats + PersonSerializer
val output = parse(testJson1).as[Person]
println(output.user)
}
I am getting an error saying that
Error:(50, 35) No JSON deserializer found for type com.examples.json4s.Person. Try to implement an implicit Reader or JsonFormat for this type.
val output = parse(testJson1).as[Person]
Not sure if I answer your question. I provide the runnable code:
import org.json4s._
import org.json4s.jackson.JsonMethods._
case class Person(
user: Option[String],
address: Address,
children: List[Child]
)
case class Address(
street: String,
city: String,
country: Country
)
case class Country(
code: String
)
case class Child(
name: String,
age: Int
)
val s =
"""
{ "user": null,
"address": {
"street": "Bulevard",
"city": "Helsinki",
"country": {
"code": "CD" }
},
"children": [
{
"name": "Mary",
"age": 5,
"birthdate": "2004-09-04T18:06:22Z"
},
{
"name": "Mazy",
"age": 3
}
]
}
"""
implicit val formats : Formats = DefaultFormats
parse(s).extract[Person] // Person(None,Address(Bulevard,Helsinki,Country(CD)),List(Child(Mary,5), Child(Mazy,3)))

iterating over a json with json4s

I have json which has a structure like below :
{
"searchResults": {
"searchCriteria": {
"location": {
"originalLocation": null
},
"startAndEndDate": {
"start": "2016-10-06T00:00:00",
"end": "2016-10-09T00:00:00"
},
"solution": [{
"resultID": "O1MDc1MD",
"selected": false,
"charges": {
"localCurrencyCode": "USD",
"averagePricePerNight": 153
},
"starRating": 3.5
},
{
"resultID": "0MDc1MD",
"selected": false,
"charges": {
"localCurrencyCode": "USD",
"averagePricePerNight": 153
},
"starRating": 3.5
}
]
}
}
}
I have a case class like :
case class ResponseModel(
//localCurrencyCode: Option[String],
averagePricePerNight: Option[Int],
starRating: Option[Int])
I want to extract the values of averagePricePerNight and starRating and return it in a List.
I am not able to get a list which includes both starRating and averagePricePerNight as within the solution array I have one more array of charges and so I dont get a List.
I used:
val messagesIds = (json \\ "solution") \ "starRating"
println(messagesIds.values)
Output: List(3.5, 3.5, 3.0)
Expected Output :
List(ResponseModel(5.0,900), ResponseModel(4.5,100), ResponseModel(4.5,1000))
and it gives me a list of StarRatings alone. How can I combine both and let me know if another library can do this easily.
I'd go for something like
case class Charges(localCurrencyCode: String, averagePricePerNight: Int)
case class Solution(resultID: String, selected: Boolean, charges: Charges, starRating: Double)
val parsed = (parse(json) \\ "solution").extract[List[Solution]]
val result = parsed.map(x => ResponseModel(Some(x.charges.averagePricePerNight), Some(x.starRating)))
Note 1: starRating seems to be a Double not an Int
Note 2: your ResponseModel uses Options... but your expected seems not to.