Transform JSON using Scala - 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

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.

How to rearrange map keys in dart?

I have a map like below :
{
"Future": [
{
"accountId": 57,
"firstName": "Inez",
"lastName": "Mitchell",
"middleName": "J",
}
],
"Overdue": [
{
"accountId": 5,
"firstName": "Mak",
"lastName": "Mitchell",
"middleName": "M",
}
],
"Due Today": [
{
"accountId": 59,
"firstName": "Jack",
"lastName": "Daniel",
"middleName": "P",
}
]
}
and wanted the map like in below order, Due Today first, Overdue 2nd and Future at last.
{
"Due Today": [
{
"accountId": 59,
"firstName": "Jack",
"lastName": "Daniel",
"middleName": "P",
}
],
"Overdue": [
{
"accountId": 5,
"firstName": "Mak",
"lastName": "Mitchell",
"middleName": "M",
}
],"Future": [
{
"accountId": 57,
"firstName": "Inez",
"lastName": "Mitchell",
"middleName": "J",
}
]
}
also these keys in length are 3 but sometimes we got only two of them means Due Today and Future but we have to make sure order is like 1. Due Today 2. Overdue 3. Future
There is no operation which simply rearranges the iteration order of a normal Dart map.
Dart maps usually default to using LinkedHashMap which orders its element (for example for iteration) in key insertion order.
If a key is already there, changing its value does not change the key's position in the iteration order, but any new key added will end up after all existing keys.
That provides the only avaialble way to change iteration order: Remove the key and add it again, which will put it at the end of the iteration order instead of where it previously was.
So, to reorder a map, the easiest is to create a new map:
var newMap = <String, List<Map<String, String>>>{};
for (var key in ["Due Today", "Overdue", "Future"]) {
if (map.containsKey(key)) newMap[key] = map[key];
}
Then newMap has the keys in the correct order. If you want to update your existing map to the same order, then you can do: map..clear()..addAll(newMap); afterwards.
If you want to avoid the extra map, you can delete keys and re-add them instead.
for (var key in ["Due Today", "Overdue", "Future"]) {
if (map.containsKey(key)) {
var value = map[key];
map.delete(key);
map[key] = value;
}
}
This should remove and re-add each key, if it's there at all, in the order you want them.
var keys = abc.keys.toList()..shuffle();
for(var k in keys) {
print('$k, ${abc[k]}');}
steps you need todo:
convert the map to a temp list
do your edits on the list
clear the map
copy the entries from the list back to map
**
> extension ExtensionsOnMap on Map<String, ChannelItem> {
> void replace(int index, String key, ChannelItem channelItem) {
>
> var tmpLst = this.entries.map((e) => MapEntry(e.key, e.value)).toList();
>
> tmpLst.removeAt(index);
> tmpLst.insert(index, new MapEntry(key, channelItem));
>
> this.clear();
>
> tmpLst.forEach((mapEntry) => this[mapEntry.key] = mapEntry.value);
> }
> }
**
Code:
void main(){
Map map = {
"Future": [
{
"accountId": 57,
"firstName": "Inez",
"lastName": "Mitchell",
"middleName": "J",
}
],
"Overdue": [
{
"accountId": 5,
"firstName": "Mak",
"lastName": "Mitchell",
"middleName": "M",
}
],
"Due Today": [
{
"accountId": 59,
"firstName": "Jack",
"lastName": "Daniel",
"middleName": "P",
}
]
};
for(String key in ['Due Today', 'Overdue', 'Future']){
var value = map[key];
map.remove(key);
map[key] = value;
}
print(map);
}
Output:
{
Due Today: [{
accountId: 59,
firstName: Jack,
lastName: Daniel,
middleName: P
}],
Overdue: [{
accountId: 5,
firstName: Mak,
lastName: Mitchell,
middleName: M
}],
Future: [{
accountId: 57,
firstName: Inez,
lastName: Mitchell,
middleName: J
}]
}

Not able to understand complex type and destructure it - reasonml

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");
}
}
...

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