How to decode Date nested in an array in circe? - scala

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")))

Related

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

~ 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.

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.

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.

Akka-HTTP JSON serialization

How does one control the deserialization for spray-json? For example, I have a class defined as:
case class A (Name:String, Value:String)
And I would like to deserialize the following JSON into a List of A objects:
{
"one": "1",
"two": "2"
}
and it should become:
List(A("one", "1"), A("two", "2"))
The problem is that the default JSON representation of that List is this one, which I do not want:
[
{ "Name": "one", "Value": "1" },
{ "Name": "two", "Value": "2" }
]
How can I accomplish this?
You can write your own custom deserializer for the structure you are looking for:
case class A(Name:String, Value:String)
implicit object ListAFormat extends RootJsonReader[List[A]] {
override def read(json: JsValue): List[A] = {
json.asJsObject.fields.toList.collect {
case (k, JsString(v)) => A(k, v)
}
}
}
import spray.json._
def main(args: Array[String]): Unit = {
val json =
"""
|{
| "one": "1",
| "two": "2"
|}
""".stripMargin
val result = json.parseJson.convertTo[List[A]]
println(result)
}
Prints:
List(A(one,1), A(two,2))