Issue with parsing nested JSON values with Lift-JSON - json

I'm using Scala 2.12 and trying to parse the below JSON file.
{
"comp1": {
"metrics": {
"operation1": {
"alias": "activity_operation",
"weight": 10
},
"operation2": {
"alias": "service_operation",
"weight": 22
}
}
},
"comp2": {
"metrics": {
"operation1": {
"alias": "activity_operation",
"weight": 14
},
"operation4": {
"alias": "service_operation",
"weight": 16
}
}
}
}
I've loaded the json into config variable, defined a case class and trying the below:
case class OperationDetails(alias: String, weight: Int)
for (detail <- (config \ "comp1").children) {
println(detail.extract[OperationDetails])
}
This gives me the error Exception in thread "main" net.liftweb.json.MappingException: No usable value for alias. Did not find value which can be converted into java.lang.String
I can't use `operation1' and retrieve children as operations are random.
I need to retrieve the operation names operation1, operation2, operation4, .. and their respective aliases and weights. Any ideas?

You are missing at least one level of nesting, and possibly also the implicit val formats.
This will print all of the operations. Note the conversion into JObject in order to be able to retrieve field names.
// Setup
case class Operation(alias: String, weight: Int)
implicit val formats = DefaultFormats
// Traversal
val comps: List[JsonAST.JValue] = config.children
for (comp <- comps) {
val metrics:List[JsonAST.JValue] = comp.children
for (metric <- metrics) {
val operations:List[JsonAST.JField] = metric.asInstanceOf[JObject].obj
for (operation <- operations) {
val op = operation.value.extract[Operation]
// Do something here
println(s"${operation.name}:(${op.alias},${op.weight})")
}
}
}
Output:
operation1:(activity_operation,10)
operation2:(service_operation,22)
operation1:(activity_operation,14)
operation4:(service_operation,16)

Related

How to add another object in JSON with Ktor

I'm creating API server with Ktor and using exposed SQL.
I have two tables like below:
/// Performance Table
#kotlinx.serialization.Serializable
data class PerformanceDAO(val performanceId: String, val title: String, val actor: String)
object Performances : Table() {
val performanceId: Column<String> = varchar("performanceId", 20)
val title: Column<String> = varchar("title", 50)
override val primaryKey = PrimaryKey(performanceId)
}
//Actor Table
object Actors: Table() {
val performanceId: Column<String> = reference("performanceId", Performances.performanceId)
val name: Column<String> = varchar("name", 10)
}
and I'm trying to get data this way:
class PerformanceDAOImpl : PerformanceDAOFacade {
private fun resultRowToPerformance(row: ResultRow) = PerformanceDAO(
performanceId = row[Performances.performanceId],
title = row[Performances.title],
actor = row[Actors.name],
)
override suspend fun allPerformances(): List<PerformanceDAO> = dbQuery {
Actors.leftJoin(Performances)
.slice(Actors.name, Performances.title, Performances.performanceId)
.selectAll()
.groupBy(Performances.title).map(::resultRowToPerformance)
}
}
and then I got only one actors without a key.
{
"performanceId": "PF13234",
"title": "Harry footer",
"actor": [
"John"
]
},
but I want get data like this
{
"performanceId": "PF13234",
"title": "Harry footer",
"actor": [
{
"name: "John",
"image": "/upload/images/image.jpg"
},
{
"name: "Harry",
"image": "/upload/images/image.jpg"
},
]
},
I want to know how to make sub object in JSON with Exposed SQL!
You have to create a separate data class ActorDTO and map it in your code by making subquery. Also, I would advice to make Exposed Entities and then map them to your DAO, it can be much simpler but will add some boilerplate code.
Please check documentation

Tornadofx REST client

I have followed an example shown here
link
And i got the hang of it, i managed to create my own "Employee" entity and i found some dummy api data online to play with.
like this Problem is, the tornadofx throws null pointer error, and i think its because the rest response sends something like this
{
"status": "success",
"data": [
{
"id": "1",
"employee_name": "Tiger Nixon",
"employee_salary": "320800",
"employee_age": "61",
"profile_image": ""
},
but when i use mocky and provide JUST the json part
[
{
"id": "1",
"employee_name": "Tiger Nixon",
"employee_salary": "320800",
"employee_age": "61",
"profile_image": ""
},...]
it all works fine.
I think those additional fields "status" and "success" in response confuse the rest client of tornadofx, and i cant manage to get it to work, is there anyway to tell client to ignore every other fields besides those of json data.
All links are functional, so you can try yourself.
full working example
package com.example.demo.view
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import javafx.scene.layout.BorderPane
import tornadofx.*
import javax.json.JsonObject
class Employee (id:Int?=null , name: String? = null, age: Int?=null): JsonModel {
val idProperty = SimpleIntegerProperty(this, "id")
var id by idProperty
val ageProperty = SimpleIntegerProperty(this, "age")
var age by ageProperty
val employeeNameProperty = SimpleStringProperty(this, "name", name)
var name by employeeNameProperty
override fun updateModel(json: JsonObject) {
with(json) {
id = int("id")!!
age = int("employee_age")!!
name = string("employee_name")
}
}
override fun toJSON(json: JsonBuilder) {
with(json) {
add("id", id)
add("employee_name", name)
add("employee_age", age)
}
}
}
class PersonEditor : View("Person Editor") {
override val root = BorderPane()
val api : Rest by inject()
var persons = listOf(Employee(1,"John", 44), Employee(2,"Jay", 33)).observable()
val model = PersonModel(Employee())
init {
api.baseURI = "https://run.mocky.io/v3/"
val response = api.get("f17509ba-2d12-4c56-b441-69ab23302e43")
println(response.list())
println(response.list().toModel<Employee>()[0].name)
// print( p.get(1))
with(root) {
center {
tableview(response.list().toModel<Employee>()) {
column("Id", Employee::idProperty)
column("Name", Employee::employeeNameProperty)
column("Age", Employee::ageProperty)
// Update the person inside the view model on selection change
model.rebindOnChange(this) { selectedPerson ->
item = selectedPerson ?: Employee()
}
}
}
right {
form {
fieldset("Edit person") {
field("Id") {
textfield(model.id)
}
field("Name") {
textfield(model.name)
}
field("Age") {
textfield(model.age)
}
button("Save") {
enableWhen(model.dirty)
action {
save()
}
}
button("Reset").action {
model.rollback()
}
}
}
}
}
}
private fun save() {
// Flush changes from the text fields into the model
model.commit()
// The edited person is contained in the model
val person = model.item
// A real application would persist the person here
println("Saving ${person.employeeNameProperty} / ${person.ageProperty}")
}
}
class PersonModel(person: Employee) : ItemViewModel<Employee>(person) {
val id = bind(Employee::idProperty)
val name = bind(Employee::employeeNameProperty)
val age = bind(Employee::ageProperty)
}
if you replace base url and send request to http://dummy.restapiexample.com/api/v1/employees you will get an error that i am talking about
Your call to mocky returns a list, so .list() works fine. Your call to restapiexample, however, returns an object, not a list, so .list() won't do what you expect. You can probably use something like this, though I haven't tested it:
response.one().getJsonArray("data").toModel<Employee>()[0].name)
Further explanation:
If you're not familiar with the structure of JSON, check out the diagrams on the JSON homepage.
TornadoFX has two convenience functions for working with JSON returns: .list() and .one(). The .list() function will check if the result is a JsonArray. If so, it simply returns it. If it is instead a JsonObject, it wraps that object in a list and returns the new list.
In your case, since restapiexample is returning an object, the result of your call to .list() is a JsonArray with a single object. It looks something like this:
[
{
"status": "success",
"data": [...]
}
]
Obviously that single object cannot be converted to an Employee, so dereferencing anything off of it will result in a NullPointerException.
The .one() function on the other hand will check if the response is a JsonObject. If it is, it simply returns the object. If, however, the response is a JsonArray, it will take the first item from the array and return that item.

Restructure a json in scala play

The following code gets the request body and validates and creates a json:
object ValidateDBConfigJson {
implicit val reads: Reads[ValidateDetails] = (
(JsPath \ "name").read[String].filter(JsonValidationError("Invalid name"))(_.length > 0) and
(JsPath \ "email").read[String].filter(JsonValidationError("Invalid email"))(_.length > 0) and
)(ValidateDetails.apply _)
}
def index() = Action { implicit request =>
val bodyAsJson = request.body.asJson.get
bodyAsJson.validate[ValidateDetails] match {
case success: JsSuccess[ValidateDetails] => {
Ok(Json.parse("succeeded!"))
}
case JsError(error) =>
BadRequest(JsError.toJson(error))
}
}
The json looks like this:
{
"obj.name": [
{
"msg": [
"error.expected.jsstring"
],
"args": []
}
],
"obj.email": [
{
"msg": [
"Invalid email"
],
"args": []
}
]
}
I want structured in the following format:
{
"ErrorMessages" :
[
"error.expected.jsstring",
"Invalid email"
]
}
Preamble: when I am parsing JSON using Play I prefer to use case class/objects rather than implicit reads, so this answer will cover that method of doing this. There may be a simpler method of doing this with implicit reads but I am not as familiar with implicit reads.
Firstly, define a case class for everything you will be taking from JSON:
object Input {
case class Err(msg: Seq[String], args: Seq[String])
object Err {
implicit val format: OFormat[Err] = Json.format[Err]
}
case class ValidateDetails(`obj.name`: Seq[Err], `obj.email`: Seq[Err])
object ValidateDetails {
implicit val format: OFormat[ValidateDetails] = Json.format[ValidateDetails]
}
}
Note: Play won't know how to handle user-defined case classes so I've made one for Err as well. The implicit val format: OFormat[ValidateDetails] = Json.format[ValidateDetails] and implicit val format: OFormat[Err] = Json.format[Err] lines are magic and do all of the reads/writes for you.
Next, define a case class for your output JSON and define a function which will turn your input case class into your output one:
object Output {
case class OutputJson(`ErrorMessages`: Seq[String])
object OutputJson {
implicit val format: OFormat[OutputJson] = Json.format[OutputJson]
}
// take msg Seq from name & email and add together into single Seq
def inputToOutput(input: Input.ValidateDetails): OutputJson = {
OutputJson(input.`obj.name`.flatMap(_.msg) ++ input.`obj.email`.flatMap(_.msg))
}
}
Finally, put this into a method which maps to a POST route in your routes file:
def index() = Action { implicit request =>
val bodyAsJson = request.body.asJson.get
bodyAsJson.validate[Input.ValidateDetails] match {
case success: JsSuccess[Input.ValidateDetails] =>
// turn the JSON into the Output case class and parse that as JSON
val output: JsValue = Json.toJson(Output.inputToOutput(success.value))
Ok(output)
case JsError(error) =>
BadRequest(JsError.toJson(error))
}
}
Now, if you run the Play app on port 9000 and POST to http://localhost:9000/ with the below JSON body...
{
"obj.name": [
{
"msg": [
"error.expected.jsstring"
],
"args": []
}
],
"obj.email": [
{
"msg": [
"Invalid email"
],
"args": []
}
]
}
...the output will be:
{
"ErrorMessages": [
"error.expected.jsstring",
"Invalid email"
]
}
I hope this answers your question.

How to get the exact json node instance using groovy?

Input
Json file :
{
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{
"value": "New",
"onclick": ["CreateNewDoc()","hai"],
"newnode":"added"
}
]
}
}
}
Groovy code :
def newjson = new JsonSlurper().parse(new File ('/tmp/test.json'))
def value=newjson.menu.popup.menuitem.value
def oneclick=newjson.menu.popup.menuitem.onclick
println value
println value.class
println oneclick
println oneclick.class
Output:
[New]
class java.util.ArrayList
[[CreateNewDoc(), hai]]
class java.util.ArrayList
Here,
The json nodes which carries String and List returns the same class name with the groovy code above shown.
How can i differentiate that nodes value and oneclick. Logically I expect value should be a instance of String. but both returns as ArrayList.
How to get the exact type of node in json using groovy.
Update 1:
I don't exactly know, can do this like shown below. My expectation to get the results this,
New
class java.util.String
[CreateNewDoc(), hai]
class java.util.ArrayList
Here you go:
In the below script using closure to show the details of each value and its type
Another closure is used to show the each map in the menuitem list.
def printDetails = { key, value -> println "Key - $key, its value is \"${value}\" and is of typpe ${value.class}" }
def showMap = { map -> map.collect { k, v -> printDetails (k,v) } }
def json = new groovy.json.JsonSlurper().parse(new File('/tmp/test.json'))
def mItem = json.menu.popup.menuitem
if (mItem instanceof List) {
mItem.collect { showMap it }
}
println 'done'
You can quickly try the same online demo
menuitem is list, so you need to get property on concrete list element:
assert newjson.menu.popup.menuitem instanceof List
assert newjson.menu.popup.menuitem[0].value instanceof String
assert newjson.menu.popup.menuitem[0].onclick instanceof List
in your json the menuitem contains array of one object:
"menuitem": [
{
"value": "New",
"onclick": ["CreateNewDoc()","hai"],
"newnode":"added"
}
]
and when you try to access menuitem.value groovy actually returns a list of value attributes for all objects in menuitem array.
that's why menuitem.value returns array ["New"]
in this case
"menuitem": [
{
"value": "New",
"onclick": ["CreateNewDoc()","hai"],
"newnode":"added"
},
{
"value": "Old",
"onclick": ["CreateOldDoc()","hai"],
"newnode":"added"
}
]
menuitem.value will return array ["New", "Old"]
but menuitem[0].value will return the string value "New"
so in your groovy code to get attributes of first menu item:
def value=newjson.menu.popup.menuitem[0].value
def oneclick=newjson.menu.popup.menuitem[0].onclick

JSON validation using Scala

I am trying to write a scala application for JSON validation. I have a Animals.scala class that defines the following:
case class Animals (id: Int, type: String, targets: String)
object Animals {
implicit val reads: Reads[Animals] = (
(JsPath \ "id").read[Int] and
(JsPath \ "type").read[String] and
(JsPath \ "targets").read[String])(Animals.apply _)
}
I have Application.scala where I have tried to validate an incoming JSON against the case class.
object Application extends Controller {
// action for JSON validation
def validateRequest = Action { implicit request =>
// this will fail if the request body is not a valid json value
val bodyAsJson = request.body.asJson.get
bodyAsJson.validate[Animals] match {
case success: JsSuccess[Animals] => {
val id = success.get.id
Ok("Validation passed! id is "+ id)
}
case JsError(error) => BadRequest("Validation failed!")
}
}
}
And finally here's my JSON input:
{
"id" : 1,
"type" : "domestic",
"targets": {
"AND": [
{
"breed": ["greyhound", "dalmatian"]
},
{
"NOT": {
"color": ["amber", "pale_amber", "black"]
}
},
{
"zipcode": ["90210", "90211"]
}
]
}
}
And I get the following error:
JsError(List((/targets,List(ValidationError(error.expected.jsarray,WrappedArray())))))
I do realize that the error is thrown because targets field is not as simple as a String compared to my JSON. How do I wrap it so that the validation passes? Should I do List[List[String]] or something along those lines?
If you don't care about the structure of targets read it as a JsObject. It will parse any internal structure that way.