Deserialize JSON based on Field in Payload in Scala - json

I have a similar question with Deserialize json based on fields in .Net (C#), but do it in Scala.
I have an app which streams in 2 types of json objects (Account and User).
Account:
{
"data_type": "account",
"id": 1,
"type": "Trial",
"created_at": 1523982003,
}
User:
{
"data_type": "user",
"id": 1,
"account_id": 1,
"department": "Finance"
"created_at": 1523982122
}
I need to deserialize the two above json objects based on the field data_type in Scala with help of Circe library.
How can I do this?

This snippet works for me in Ammonite:
import $ivy.`io.circe:circe-core_2.12:0.9.3`, io.circe._
import $ivy.`io.circe:circe-generic_2.12:0.9.3`, io.circe.generic._
import $ivy.`io.circe:circe-generic-extras_2.12:0.9.3`, io.circe.generic.extras._
interp.load.plugin.ivy("org.scalamacros" % "paradise_2.12.4" % "2.1.1")
implicit val config: Configuration = Configuration.
default.
withSnakeCaseMemberNames.
withDiscriminator("data_type").
copy(transformConstructorNames = _.toLowerCase)
{
#ConfiguredJsonCodec
sealed trait InputEntity
object InputEntity {
#ConfiguredJsonCodec case class Account(id: Long, `type`: String, createdAt: Long) extends InputEntity
#ConfiguredJsonCodec case class User(id: Long, accountId: Long, department: String, createdAt: Long) extends InputEntity
}
}
import $ivy.`io.circe:circe-parser_2.12:0.9.3`, io.circe.parser._
val accountJson = """
{
"data_type": "account",
"id": 1,
"type": "Trial",
"created_at": 1523982003
}
"""
val account = decode[InputEntity](accountJson)
// account: Either[Error, InputEntity] = Right(Account(1L, "Trial", 1523982003L)
val userJson = """
{
"data_type": "user",
"id": 1,
"account_id": 1,
"department": "Finance",
"created_at": 1523982122
}
"""
val user = decode[InputEntity](userJson)
// user: Either[Error, InputEntity] = Right(User(1L, 1L, "Finance", 1523982122L))
(BTW: you had syntax errors in your JSON examples that would make the parser fail, so I fixed them in code above).
The most important here is
Configuration from io.circe.generic.extras._ that defines discriminator field,
keeping classes as sum type,
if you use annotations for generating codecs replace #JsonCodec with #ConfiguredJsonCodec.
Actually, you could also replace these Strings with enums, and read createdAt as LocalDateTime or similar, but that would be out of scope of this question.

Related

Moshi parse json with different key

I was looking towards PolymorphicAdapter but all the polymorphic example I could find had a key called "type" or something similar that could be use to differentiate the class to use. However in my case I don't have such key. I'm a bit lost on how to parse such a peculiar json.
{
"infos": {
"1588318": {
"id": "1588318",
"id_user": "9701",
"profile_name": "Profile1",
"views": 100
},
"1588319": {
"id": "1588319",
"id_user": "7391",
"profile_name": "Profile2",
"views": 10
},
"1588320": false,
"1588321": {
"id": "1588321",
"deleted": true
}
}
}
data class UserInfo(val infos: Map<String, UserResult>)
sealed class UserResult {
data class UserDeleted(val id: String, val deleted: Boolean): UserResult()
data class UserInfoCard(
val id: String,
val title: String,
#Json(name = "profile_name") val profileName: String,
val views: Int
): UserResult()
}
In the end I didn't find any solution and after discussing with the API manager he said he would update with a key to determine if it's either a profile or a deleted_profile

How do I parse the json from thingspeak to get the field value using klaxon on android studio?

I'm using thingspeak and I have successfully got thingspeak to fetch the json data using okhttp but I don't know how to parse it correctly using klaxon.
Here is the code
private fun funButton1() {
println("Attempting to get JSON data!")
val url = "https://api.thingspeak.com/channels/1029606/feeds.json?results=1"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
println(body)
class feeds (val field1: String)
val result = Klaxon()
.parse<feeds>(body.toString())
textView.text = result
}
override fun onFailure(call: Call, e: IOException) {
println("Failed to execute request!")
}
})
This is the json data from the thingspeak
{
"channel": {
"id": 1029606,
"name": "LED ",
"description": "Acts as a medium for the phone and arduino \r\nRules : 1 = LED ON 0 = LED OFF ",
"latitude": "0.0",
"longitude": "0.0",
"field1": "LED STATUS",
"created_at": "2020-04-01T17:19:03Z",
"updated_at": "2020-04-01T17:20:39Z",
"last_entry_id": 25
},
"feeds": [
{
"created_at": "2020-05-11T02:58:07Z",
"entry_id": 25,
"field1": "1"
}
]
}
Im trying to get the value of field1 which the value is one but I don't know how I'm supposed to do that because im stupid. But I'm hoping that someone could show me how to use klaxon properly to get the json data.
For Klaxon, you'll need to create a class which represent the structure of your JSON.
f.e. if you get a JSON with:
{
"username": "admin",
"password": "admin"
}
you wanna make a class which looks like that:
class myClass(val username:String, val password:String)
Then, you can parse it like you are doing.
For your JSON, you'll need a bigger Class.
For the Sake of simplicity, I'll just make a class for feeds and a class for the channel:
class Feed(val created_at:String, val entry_id: Int, val field1:String)
class Channel(val id: Int, val name: String, val description: String, val latitude: String, val longitude:String, val field1: String, val created_at: String, val updated_at: String, val last_entry_id: Int)
Then you can use this class to parse your JSON:
class Thingspeak(val channel: Channel, val feeds: ArrayList<Feed>)
Please let me know if it worked for you!

Read a JSON and parse the contents and display the result in Scala

I am a newbie in scala. i try to read a json and parse it using json4s library.
Already written the case class and code for reading and parsing the sample json file.
I need to iterate the json and print the details of each attribute's.
Case Class
case class VehicleDetails(
name: String,
manufacturer: String,
model: String,
year: String,
color: String,
seat: Int,
variants: Seq[String],
engine: Int,
dealer: Map[String, String],
franchise: Map[String, String])
The json data and the code i tried is given below.
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.DefaultFormats
object CarDetails extends App {
val json = parse("""{
"vehicle_details": [
{
"CAR": {
"name": "Brezza",
"manufacturer": "Maruti",
"model": "LDI",
"year": 2019,
"color": "Blue",
"seat": 5,
"engine": 1,
"cylinder": 4,
"variants": [
"LDI",
"LDI(O)",
"VDI",
"VDI(O)",
"ZDI",
"ZDI+"
],
"dealer": {
"kerala": "Popular"
},
"franchise": {
"ekm": "popular_ekm"
}
},
"SUV": {
"name": "Scross",
"manufacturer": "Maruti",
"model": "LDI",
"year": 2020,
"color": "Blue",
"variants": [
"LDI",
"VDI",
"ZDI"
],
"dealer": {
"kerala": "Popular"
},
"franchise": {
"ekm": "popular_ekm"
}
}
}
]
}""")
implicit val formats = DefaultFormats
val definition = json.extract[VehicleDetails.Definition]
val elements = (json \\ "vehicle_details").children
This pretty close, just a few small changes needed.
First, create a class that encapsulates all the JSON data:
case class AllDetails(vehicle_details: List[Map[String, VehicleDetails]])
Then just extract that class from the json
implicit val formats = DefaultFormats
val details = Extraction.extract[AllDetails](json)
With this particular JSON the seat and engine fields are not present in all the records so you need to modify VehicleDetails to make these Option values:
case class VehicleDetails(
name: String,
manufacturer: String,
model: String,
year: String,
color: String,
seat: Option[Int],
variants: Seq[String],
engine: Option[Int],
dealer: Map[String, String],
franchise: Map[String, String]
)
[ Other values that might be omitted in other records will also need to be Option values ]
You can unpick the result using standard Scala methods. For example
res.vehicle_details.headOption.foreach { cars =>
val typeNames = cars.keys.mkString(", ")
println(s"Car types: $typeNames")
cars.foreach { case (car, details) =>
println(s"Car type: $car")
println(s"\tName: ${details.name}")
val variants = details.variants.mkString("[", ", ", "]")
println(s"\tVariants: $variants")
}
}
To get back to the raw JSON, use Serialization:
import org.json4s.jackson.Serialization
val newJson = Serialization.write(res)
println(newJson)

Parse JSON with unknown attributes names into a Case Class

I have the following JSON file to be parsed into a case class:
{
"root": {
"nodes": [{
"id": "1",
"attributes": {
"name": "Node 1",
"size": "3"
}
},
{
"id": "2",
"attributes": {
"value": "4",
"name": "Node 2"
}
}
]
}
}
The problem is that the attributes could have any value inside it: name, size, value, anything ...
At this moment I have defined my case classes:
case class Attributes(
name: String,
size: String,
value: Sting
)
case class Nodes(
id: String,
attributes: Attributes
)
case class Root(
nodes: List[Nodes]
)
case class R00tJsonObject(
root: Root
)
Whats is the best way to deal with this scenario when I can receive any attribute ?
Currently I am using Json4s to handle son files.
Thanks!
Your attributes are arbitrarily many and differently named, but it seems you can store them in a Map[String, String] (at least, if those examples are anything to go by). In this case, using circe-parser (https://circe.github.io/circe/parsing.html), you could simply use code along these lines in order to convert your JSON directly into a simple case-class:
import io.circe._, io.circe.parser._
import io.circe.generic.semiauto._
case class Node(id: String, attributes: Map[String,String])
case class Root(nodes: List[Node])
implicit val nodeDecoder: Decoder[Node] = deriveDecoder[Node]
implicit val nodeEncoder: Encoder[Node] = deriveEncoder[Node]
implicit val rootDecoder: Decoder[Root] = deriveDecoder[Root]
implicit val rootEncoder: Encoder[Root] = deriveEncoder[Root]
def myParse(jsonString: String) = {
val res = parse(jsonString) match {
case Right(json) => {
val cursor = json.hcursor
cursor.get[Root]("root")
}
case _ => Left("Wrong JSON!")
}
println(res)
}
This snippet will print
Right(Root(List(Node(1,Map(name -> Node 1, size -> 3)), Node(2,Map(value -> 4, name -> Node 2)))))
on the console, for the JSON, you've given. (Assuming, the solution doesn't have to be in Json4s.)

How to deserialize json with json-lenses

What is the best practice to deserialize JSON to a Scala case class using json-lenses?
some.json :
[
{
"id": 1,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
},
{
"id": 3,
"name": "Chris"
}
]
some case class :
case class Foo(id: Long, name: String)
What's best way to convert the json in some.json to List[Foo] ?
json-lenses supports spray-json and with spray-json you could do:
import spray.json._
case class Foo(id: Long, name: String)
object JsonProtocol extends DefaultJsonProtocol {
implicit val FooFormat = jsonFormat2(Foo)
}
import JsonProtocol._
val source = scala.io.Source.fromFile("some.json")
val json = try source.mkString.parseJson finally source.close()
json.convertTo[List[Foo]]
// List[Foo] = List(Foo(1,Alice), Foo(2,Bob), Foo(3,Chris))