Extract list of fields from Nested Json using Json4s Scala - json

I am trying to parse the nested Json and get the list of name from the fields tag.
{
"subject": "table-name",
"version": 1,
"id": 234,
"schema": "{
\"type\":\"record\",\"name\":\"table-name\",
\"fields\":[
{\"name\":\"NAME\",\"type\":\"CHARACTER\},
{\"name\":\"EID\",\"type\":\"INT\"},
{\"name\":\"DEPARTMENT\",\"type\":\"CHARACTER\"}
]
}"
}
I checked couple of post and came up with below code. I can get the schema definition(which is fairly easy) but not able to the list of name from fields.
case class Names(name : String)
case class FieldNames(fields: List[Names])
case class ColNames(subject: String, schema: FieldNames)
implicit val formats = DefaultFormats
val sourceSchema = parse("JsonStr").extract[ColNames]
println(sourceSchema)
My idea was the part schema: FieldNames will get me the fields tag and then List[Names] will get me the list of name but all I get is empty list. If I change the schema: FieldNames with schema: String I get the schema too but I am not getting how to get the required list of name after that. Below is the current output:
ColNames(table-name,FieldNames(List()))
Update:
I got it working but I ended up parsing the same Json twice which I don't feel is a best way of doing it. I would still like to know the best way to get it done. Below is my current solution:
case class Name(name : String)
case class FieldNames(fields: List[Name])
case class ColNames(subject: String, schema: String)
val sourceSchema = parse("JsonStr").extract[ColNames]
val cols= parse(sourceSchema.schema).extract[FieldNames]
Output:
FieldNames(List(Name(NAME), Name(EID), Name(DEPARTMENT)))

Related

PlayJSON in Scala

I am trying to familiarize myself with the PlayJSON library. I have a JSON formatted file like this:
{
"Person": [
{
"name": "Jonathon",
"age": 24,
"job": "Accountant"
}
]
}
However, I'm having difficulty with parsing it properly due to the file having different types (name is a String but age is an Int). I could technically make it so the age is a String and call .toInt on it later but for my purposes, it is by default an integer.
I know how to parse some of it:
import play.api.libs.json.{JsValue, Json}
val parsed: JsValue = Json.parse(jsonFile) //assuming jsonFile is that entire JSON String as shown above
val person: List[Map[String, String]] = (parsed \ "Person").as[List[Map[String, String]]]
Creating that person value throws an error. I know Scala is a strongly-typed language but I'm sure there is something I am missing here. I feel like this is an obvious fix too but I'm not quite sure.
The error produced is:
JsResultException(errors:List(((0)/age,List(JsonValidationError(List(error.expected.jsstring),WrappedArray())))
The error you are having, as explained in the error you are getting, is in casting to the map of string to string. The data you provided does not align with it, because the age is a string. If you want to keep in with this approach, you need to parse it into a type that will handle both strings and ints. For example:
(parsed \ "Person").validate[List[Map[String, Any]]]
Having said that, as #Luis wrote in a comment, you can just use case class to parse it. Lets declare 2 case classes:
case class JsonParsingExample(Person: Seq[Person])
case class Person(name: String, age: Int, job: String)
Now we will create a formatter for each of them on their corresponding companion object:
object Person {
implicit val format: OFormat[Person] = Json.format[Person]
}
object JsonParsingExample {
implicit val format: OFormat[JsonParsingExample] = Json.format[JsonParsingExample]
}
Now we can just do:
Json.parse(jsonFile).validate[JsonParsingExample]
Code run at Scastie.

Kotlin - Array property in data class error

I'm modelling some JSON - and using the following lines
data class Metadata(
val id: String,
val creators: Array<CreatorsModel>
)
along with:
data class CreatorsModel (
val role: String,
val name: String
)
However keep seeing the error: Array property in data class error.
Any ideas why this is?
FYI, the JSON looks like:
{
"id": "123",
"creators": [{
"role": "Author",
"name": "Marie"
}
]
}
In Kotlin you should aim to use List instead of Array where possible. Array has some JVM implications, and although the compiler will let you, the IDE may prompt you to override equals and hashcode manually. Using List will make things much simpler.
You can find out more about the difference here: Difference between List and Array types in Kotlin

Play Json Parser: How to ignore field during json parser

I am using Scala with play JSON library for parsing JSON. We are the facing the problem using JSON parsing is, we have same JSON structure, but some JSON files contain different with values structure with the same key name. Let's take an example:
json-1
{
"id": "123456",
"name": "james",
"company": {
"name": "knoldus"
}
}
json-2
{
"id": "123456",
"name": "james",
"company": [
"knoldus"
]
}
my case classes
case class Company(name: String)
object Company{
implicit val _ = Json.format[Company]
}
case class User(id: String, name: String, company: Company)
object User{
implicit val _ = Json.format[Company]
}
while JSON contains company with JSON document, we are getting successfully parsing, but if company contains an array, we are getting parsing exception. Our requirements, are is there anyway, we can use play JSON library and ignore the fields if getting parsing error rather that, ignore whole JSON file. If I am getting, company array values, ignore company field and parse rest of them and map corresponding case class.
I would do a pre-parse function that will rename the 'bad' company.
See the tutorial for inspiration: Traversing-a-JsValue-structure
So your parsing will work, with this little change:
case class User(id: String, name: String, company: Option[Company])
The company needs to be an Option.
Final we found the answer to resolving this issue, as we know, we have different company structure within JSON, so what we need to do, we need to declare company as a JsValue because in any case, whatever the company structure is, it is easily assigned to JsValue type. After that, our requirements are, we need to use object structure, and if JSON contains array structure, ignore it. After that, we used pattern matching with our company JsValue type and one basis of success and failure, we parse or JSON. The solution with code is given below:
case class Company(name: String)
object Company{
implicit val _ = Json.format[Company]
}
case class User(id: String, name: String, company: JsValue)
object User{
implicit val _ = Json.format[Company]
}
Json.parse("{ --- whatevery json--string --- }").validate[User].asOpt match {
case Some(obj: JsObject) => obj.as[Company]
case _ => Company("no-name")
}

Scala Play Json JSResultException Validation Error

I am trying to pass this json string to my other method but SOMETIMES I get this error,
play.api.libs.json.JsResultException:
JsResultException(errors:List((,List(ValidationError(error.expected.jsstring,WrappedArray())))))
I find it strange that this occurs randomly, sometimes I don't get the exception and sometimes I do. Any ideas?
Here is what my json looks like
val string = {
"first_name" : {
"type" : "String",
"value" : "John"
},
"id" : {
"type" : "String",
"value" : "123456789"
},
"last_name" : {
"type" : "String",
"value" : "Smith"
}
}
I read it like
(string \ "first_name").as[String]
(string \ "first_name") gives JsValue not JsString so as[String] does not work.
But if you need first name value you can do
val firstName = ((json \ "first_name") \ "value").as[String]
This is another option to consider for type safety while reading and writing Json with play Json API. Below I am showing using your json as example reading part and writing is analogous.
Represent data as case classes.
case Person(id:Map[String,String], firstName:Map[String,String], lastName:[String,String]
Write implicit read/write for your case classes.
implicit val PersonReads: Reads[Person]
Employ plays combinator read API's. Import the implicit where u read the json and you get back the Person object. Below is the illustration.
val personObj = Json.fromJson[Person](json)
Please have a look here https://www.playframework.com/documentation/2.6.x/ScalaJsonAutomated
First of all you can't traverse a String object, so your input must be converted to JsValue to be able to traverse it:
val json = Json.parse(string)
then the first_name attribut is of JsValue type and not a String so if you try to extract the first_name value like you do you will get always a JsResultException.
I can only explain the random appearance of JsResultException by a radom change in your first_name field json structure. make sur your sting input to traverse has always a fixed first_name filed structure.

How to extract the numbers from json path using scala

My response will be like:
{
"code" : 201,
"message" : "Your Quote Id is 353541551"
}
From this above response I have to extract the number 353541551 alone, so I tried with the basic scala snippets. I tried the following:
.check((status is 201),(jsonPath("Your Quote Id is(//d{9})").saveAs("quoteid")))
And another snippet:
.check((status is 201),(jsonPath("$.message.([0-9]+)").saveAs("quoteid")))
But both didn't work
Please help me out to extract the numbers
Try to use spary json; https://github.com/spray/spray-json
You can define json data structure using scala case class and define custom apply/unapply methods if required.
case class JsonResponse(code: Int, message: String)
object JsonResponse { implicit val f = JsonFormat2(JsonResponse.apply)}
you can use scala Regex to pattern match the string to extract the required value.