Not able to understand complex type and destructure it - reasonml - reason

I use reason-apollo to fetch data from server. It returns me data of type (vscode shows me this type):
option(
Js.t(
< count : int;
rows : [ `User of
< firstName : string; id : string; lastName : string;
userName : string >
Js.t
| `Node of < id : string > Js.t ] option Js.Array.t >
)
)
I don't really understand type of "rows", and i am not able to get data from that. I tried this:
switch response##users {
| None => ReasonReact.string("none")
| Some(data) => {
data##rows |> Array.map(optionalRow => {
switch optionalRow {
| None => ReasonReact.string("none")
| Some(row) => ReasonReact.string(row##firstName);
}
});
ReasonReact.string("test");
}
};
but error is following:
This has type:
array(option(Js.t(({.. firstName: string} as 'a)))) =>
array(ReasonReact.reactElement)
But somewhere wanted:
Js.Array.t(option([ `Node({. "id": string})
| `User({. "firstName": string, "id": string,
"lastName": string, "userName": string}) ])) =>
'b
The incompatible parts:
array(option(Js.t('a)))
vs
Js.Array.t(option([ `Node({. "id": string})
| `User({. "firstName": string, "id": string,
"lastName": string, "userName": string}) ]))
(defined as
array(option([ `Node({. "id": string})
| `User({. "firstName": string, "id": string,
"lastName": string, "userName": string}) ])))
Further expanded:
Js.t('a)
vs
[ `Node({. "id": string})
| `User({. "firstName": string, "id": string, "lastName": string,
"userName": string}) ]
How can I get "firstName" from result?

Ahh clear, it's a plymorphic variant, here is snippet how to get the firstName.
...
switch optionalRow {
| None => ReasonReact.string("none")
| Some(row) => {
switch row {
| `User(u) => ReasonReact.string(u##firstName)
| `Node(n) => ReasonReact.string("test")
};
ReasonReact.string("test");
}
}
...

Related

Transform JSON using Scala

I have the following JSON
{
"record1": {
"firstName": "John",
"lastName": "Doe",
"locations": {
"29b2f2295cd74b8cbb53db4379f0d823": "New York"
}
},
"record2": {
"firstName": "Carol",
"lastName": "Rees",
"locations": {
"0055bb74b4984156b821ebbea6937084": "California"
}
},
"record3": {
"firstName": "Colin",
"lastName": "Scott",
"locations": {
"aba67f566fc24f8a8eb3165648ca5e4f": "Toronto",
"b847750c565246638dbc72cb89ead227": "London"
}
}
}
which needs to be transformed to the following using Scala
{
"record1": {
"firstName": "John",
"lastName": "Doe",
"locations": [{
"id" : "29b2f2295cd74b8cbb53db4379f0d823",
"location": "New York"
}]
},
"record2": {
"firstName": "Carol",
"lastName": "Rees",
"locations": [{
"id" : "0055bb74b4984156b821ebbea6937084",
"location": "California"
}]
},
"record3": {
"firstName": "Colin",
"lastName": "Scott",
"locations": [{
"id": "aba67f566fc24f8a8eb3165648ca5e4f",
"location:" : "Toronto"
},
{
"id" : "b847750c565246638dbc72cb89ead227",
"location": "London"
}]
}
}
I am being new to scala, this is what I have so far
case class PersonEntry(firstName: String, lastName: String, locations: Map[String, String])
val jsonMapper = JsonMapper.builder().addModule(DefaultScalaModule).build()
val inputJson: String = "{\n \"record1\": {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"locations\": {\n \"29b2f2295cd74b8cbb53db4379f0d823\": \"New York\"\n }\n },\n \"record2\": {\n \"firstName\": \"Carol\",\n \"lastName\": \"Rees\",\n \"locations\": {\n \"0055bb74b4984156b821ebbea6937084\": \"California\"\n }\n },\n \"record3\": {\n \"firstName\": \"Colin\",\n \"lastName\": \"Scott\",\n \"locations\": {\n \"aba67f566fc24f8a8eb3165648ca5e4f\": \"Toronto\",\n \"b847750c565246638dbc72cb89ead227\": \"London\"\n }\n }\n}"
val parsedPerson = jsonMapper.readValue(inputJson, classOf[Map[String, Any]])
val personMap: Map[String, PersonEntry] = parsedPerson.mapValues(jsonMapper.convertValue(_, classOf[PersonEntry]))
There are several ways to do it. As you already have written a case class representing the input data, I would:
create a case class representing the output data
parse input as input case classes
map input case class to output case class
generate JSON for output case classes
If performance matters, you could look to work without parsing to case classes and operate directly on the JSON input but I won't explain this option.
That is, something along the lines of:
case class PersonOutput(firstName: String, lastName: String, locations: Seq[Location])
case class Location(id: String, location: String)
val output: Map[String, PersonOutput] = jsonMapper
.readValue(inputJson, classOf[Map[String, PersonEntry]])
.map { case (key, person) =>
val newLocations = person.locations.map {
case (k, v) => Location(k, v)
}
key -> PersonOutput(person.firstName, person.lastName, newLocations)
}
// Then generate JSON for output

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.

WSO2 stream processor, json error "contains missing attributes" with kafka

I am using kafka to get data to WSO2 stream processor in Json format, but I am getting the error "contains missing attributes.Hence dropping the message" error on every json file I send to wso2 kafka topic.
I have downloaded latest JSON siddhi connector "https://store.wso2.com/store/assets/analyticsextension/details/0e6a6b38-f1d1-49f5-a685-e8c16741494d" and replaced in my wso2/lib directory.
I don't get any error if I run this on simulator but only when events are published to kafka topic manually.
Below is my WSO2 Stream processor code:
#App:name('Transaction json')
/*
TransactionStream definition. It receives events from "kafka_topic" in json format.
*/
#source(type = 'kafka', topic.list = 'kafka_topic', partition.no.list = '0', threading.option = 'single.thread', group.id = 'group', bootstrap.servers = 'localhost:9092',
#map(type = 'json',enclosing.element='$',
#attributes(code = "code", name = "name",desc = "desc",transRefId ="transRefId",origAmount ="origAmount",amount = "amount",currency = "currency",requestId = "requestId",redeemedCashcode = "redeemedCashcode", sender_id ="sender.id",sender_name = "sender.name", sender_phone = "sender.phone",sender_pocket = "sender_pocket",sender_client = "sender.client",receiver_id = "receiver.id",receiver_name = "receiver.name",receiver_phone = "receiver.phone",receiver_pocket = "receiver.pocket",receiver_client = "receiver.client",beneficiary_phone = "beneficiary.phone",receiver_client = "receiver.client",beneficiary_phone = "beneficiary.phone",beneficiary_name = "beneficiary.name",beneficiary_nric = "beneficiary.nric",depositor_phone = "depositor.phone",depositor_name = "depositor.name",depositor_nric = "depositor.nric",offer = "offer",service = "service" , message = "message",status = "status",processedBy_id = "processedBy.id",processedBy_name = "processedBy.name",processedBy_phone = "processedBy.phone",processedBy_client = "processedBy.client",processedBy_owner = "processedBy.owner",processedAt = "processedAt",fees_debitFee = "fees.debitFee",fees_creditFee = "fees.creditFee",deviceId = "deviceId",isRefund ="isRefund",oldTransRefId = "oldTransRefId",linkBankTrans_err = "linkBankTrans.err",linkBankTrans_message = "linkBankTrans.message",linkBankTrans_data = "linkBankTrans.data",linkBankTrans_status = "linkBankTrans.status",linkBankTrans_request_url = "linkBankTrans.request.url",linkBankTrans_request_requestParams = "linkBankTrans.request.requestParams",linkBankTrans_action = "linkBankTrans.action",bankAccountNo = "bankAccountNo",transType = "transType",devGrp = "devGrp",createdAt = "createdAt",updatedAt = "updatedAt")))
define stream TransactioninputStream (code string, name string, desc string, transRefId string, origAmount float, amount float, currency string, requestId string, redeemedCashcode string, sender_id string, sender_name string, sender_phone string, sender_pocket string, sender_client string, receiver_id string, receiver_name string, receiver_phone string, receiver_client string, beneficiary_phone string, beneficiary_name string, beneficiary_nric string, depositor_phone string, depositor_name string,depositor_nric string, offer string, service string, message string, status string, processedBy_id string, processedBy_name string, processedBy_phone string, processedBy_client string, processedBy_owner string,processedAt string, fees_debitFee float, fees_creditFee float, deviceId string, isRefund string, oldTransRefId string, linkBankTrans_err string, linkBankTrans_message string, linkBankTrans_data string, linkBankTrans_status string, linkBankTrans_request_url string, linkBankTrans_request_requestParams string, linkBankTrans_action string, bankAccountNo string, transType string, devGrp string, createdAt string);
Reference Json:
{
"cd": "acb235dd",
"name": "Newtest",
"desc": "testing env",
"ref": "3232d3dew3",
"ora": 500000,
"amount": 500000,
"curr": "INR",
"sen": {
"id": "fdgdfgv",
"name": "dao",
"phone": "8268826483",
"pocket": "bde4gvfdgd3fd",
"cl": "try"
},
"rec": {
"id": "fsfsgfs3322",
"name": "mmv",
"phone": "76288373",
"pocket": "null",
"cl": "test"
},
"bef": {
"phone": "null",
"name": "null",
"ic": "null"
},
"dep": {
"phone": "null",
"name": "null",
"ic": "null"
},
"offer": "htgdte44",
"service": "gdrgdrgdv34",
"status": "done",
"prb": {
"id": "fdgdgd",
"name": "test",
"phone": "frgvrd",
"cl": "test",
"owner": "null"
},
"processedAt": {
"$date": "2019-09-19T10:17:05.377+0000"
},
"fees": {
"debitFee": 0,
"creditFee": 0,
},
"dId": "vdsvdd433",
"anumb": "xxxx6452",
"ttype": "normal",
"devGrp": 0,
"createdAt": {
"$date": "2019-09-19T10:17:05.381+0000"
},
"updatedAt": {
"$date": "2019-09-19T10:17:05.381+0000"
},
"_id": {
"$oid": "5d8355a1a3b8053cb768eea8"
},
"bankTrans": {
"err": "0",
"message": "successfully!",
"data": "fbsvbsgfiyshiu39",
"status": 0,
"request": {
"url": "http://localhost/testing",
"requestParams": "89743874023804832084093278327082384-329-4932-r-98-384-83-24"
},
"action": "testing"
}
}
This happens when some of the attributes are missing in the message. Here in the sample message, there is no code attribute. That's why the messages are dropped. However, you can ask siddhi JSON mapper to process messages even if attributes are missing using, fail.on.missing.attributes=false. Please see API docs of JSON mapper https://siddhi-io.github.io/siddhi-map-json/api/5.0.5/

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.