Access to JSON element in groovy map - json

I'm trying to access an element of some json returned into a map from an api call so I can pass it to another api call. I can't seem to properly create a varible and give it the value I need. Here's the returned json, I need to access the Id element.
{
"totalSize": 1,
"done": true,
"records": [
{
"attributes": {
"type": "User",
"url": "/services/data/v24.0/sobjects/User/MYIDNUMBER"
},
"Id": "MYIDNUMBER"
}
]
}
here's the restful service call I use and my attempt to access the Id element and put it in sfId so I can use it in my next API call
def http = new HTTPBuilder(instance_domain)
http.request(GET,JSON) { req ->
uri.path = "services/data/v24.0/query/"
uri.query = [q:"SELECT Id from User WHERE Email = '$loginid#myschool.edu'"]
headers['Authorization'] = "Bearer $access_token"
response.success = { resp, json ->
json.each{ key,value ->
sfMap = [sfUser: [json: json]]
}
sfId = sfMap[records.Id]
}
response.failure = { resp, json ->
println resp.status
println json.errorCode
println json.message
}
}
I get the following error on the server where the portletized version of this is deployed
2014-07-08 08:02:39,710 ERROR [http-bio-443-exec-161] portal-web.docroot.html.portal.render_portlet_jsp:154 groovy.lang.MissingPropertyException: No such property: records for class: groovyx.net.http.HTTPBuilder$RequestConfigDelegate

Based on your json structure, here's what I can say. The records is an array which potentially can contain number of objects hence number of Ids.
def json = new groovy.json.JsonSlurper().parseText ("""
{
"totalSize": 1,
"done": true,
"records": [
{
"attributes": {
"type": "User",
"url": "/services/data/v24.0/sobjects/User/MYIDNUMBER"
},
"Id": "MYIDNUMBER"
}
]
}
""")
If you are sure about first element's Id, then this will do the trick:
println json.records.first().Id
Otherwise, this might be better option which will give you Ids of all the objects in records.
println json.records.collect{ it.Id }

#kunal, that helped.
For future refernce, here's how I added the delcaration of a varible to be used later on, assigning it the value from the json responce.
def http = new HTTPBuilder(instance_domain)
http.request(GET,JSON) { req ->
uri.path = "services/data/v24.0/query/"
uri.query = [q:"SELECT Id from User WHERE Email = '$loginid#myschool.edu'"]
headers['Authorization'] = "Bearer $access_token"
response.success = { resp, json ->
sfId = json.records.first().Id <----- this did the trick
json.each{ key,value ->
sfMap = [sfUser: [json: json]]
}
}
response.failure = { resp, json ->
println resp.status
println json.errorCode
println json.message
}
}

Related

parse generic sealed class in kotlin using jackson

I have the following generic sealed class representing the status of network response.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "status")
#JsonSubTypes(
value = [
JsonSubTypes.Type(value = Response.OK::class, name = "OK"),
JsonSubTypes.Type(value = Response.Error::class, name = "ERROR"),
]
)
sealed class Response<out Content, out Error> {
data class OK<out Content>(val content: Content) : Response<Content, Nothing>()
data class Error<out Error>(val error: Error) : Response<Nothing, Error>()
}
The network response json can have status": "OK" in which case it contains a content key or a "status": "ERROR" in which case it contains a error key.
The type under the content and error key can be different for each endpoint I’m talking to. Hence the need for generic types
So for example one endpoit returns String as types, so Response<String, String>
{
"status": "OK",
"content": "Hello"
}
{
"status": "ERROR",
"error": "MyError"
}
Another endpoit return Double as content and Int as error, so
Response<Double, Int>
{
"status": "OK",
"content": 2.0
}
{
"status": "ERROR",
"error": 1
}
My parsing fails though with message
Could not resolve type id 'OK' as a subtype of `com.example.models.Response<java.lang.String,java.lang.String>`: Failed to specialize base type com.example.models.Response<java.lang.String,java.lang.String> as com.example.models.Response$OK, problem: Type parameter #1/2 differs; can not specialize java.lang.String with java.lang.Object
at [Source: (String)"{
"status": "OK",
"content": "Hello"
}"; line: 2, column: 13]
#Nested
inner class ContentParsingTests {
#Test
fun `parses OK response`() {
val json = """
{
"status": "OK",
"content": "Hello"
}
""".trimIndent()
when (val result = objectMapper.readValue<Response<String, String>>(json)) {
is Response.OK -> {
assertEquals(result.content, "Hello")
}
is Response.Error -> {
fail()
}
}
}
#Test
fun `parses ERROR response`() {
val json = """
{
"status": "ERROR",
"error": "MyError"
}
""".trimIndent()
when (val result = objectMapper.readValue<Response<String, String>>(json)) {
is Response.OK -> {
fail()
}
is Response.Error -> {
assertEquals(result.error, "MyError")
}
}
}
}
I noticed that the parsing works fine if only the content is generic:
sealed class Response<out Content > {
data class OK<out Content>(val content: Content) : Response<Content>()
object Error : Response<Nothing>()
}
but of course I loose the error payload
What would be a correct way to parse the json into my generic class?
I think the issue is with Nothing because it's like Void and you can't create an instance of it or get a type information that's why the serialization library struggling with it. so a solution for the current problem is to update the model definition like this and it works. It's not ideal though.
sealed class Response<out Content, out Error> {
data class OK<out Content, out Error>(val content: Content) : Response<Content, Error>()
data class Error<out Content, out Error>(val error: Error) : Response<Content, Error>()
}
You don't need generics at all for this case. Just have:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "status")
#JsonSubTypes(
value = [
JsonSubTypes.Type(value = Response.OK::class, name = "OK"),
JsonSubTypes.Type(value = Response.Error::class, name = "ERROR"),
]
)
sealed interface Response {
data class Success(val content: Content): Response
data class Error(val error: Error): Response
}
Jackson will then be able to parse everything correctly.

Kotlin-Volley Send JSON (post) with multiple json object in json array

i am build Point Of Sales apps and i can’t able to send multiple json object in json array. My apps read from android database (SQLite) and send multiple products to server. anyone help me ? thanks
Logcat : E/Volley: [893] BasicNetwork.performRequest: Unexpected response code 422 for https://…
override fun getHeaders(): MutableMap<String, String> {
val headers = HashMap<String, String>()
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["Authorization"] = "Bearer " + user_info.token
return headers
}
override fun getParams(): MutableMap<String,String> {
val cartRepository = CartRespository(application)
val transaksi = cartRepository.getByIdTrans(id_trans)
val jo = JSONObject()
val ja = JSONArray()
for(i in 0 until transaksi.size){
ja.put(i,jo.put("product_id", transaksi[i].id_item))
ja.put(i,jo.put("jumlah", transaksi[i].kuantitas).toString())
}
val map = HashMap<String, String>()
map.put("rfid", "122312")
map.put("device", user_info.device)
map.put("products", ja.toString())
return map
}
this is my request API body, run normaly in postman.
{
"rfid": "122312",
"device": "1233311",
"products": [
{
"product_id": 4,
"jumlah": "1"
}
]
}

How to Parse Json String received from HTTP and loop through the values

I'm using Scala and Swagger and i need help figuring out how to loop through the values in the json and use those values for checking and others.
The json string that is returned after HTTP get request looks like this:
{
"count": 3,
"items": [
{
"Id": "fd0a9e5a",
"DbName": "Xterior Prod",
"Name": "XP"
},
{
"Id": "4158a1a6",
"DbName": "Invidibi Pappear",
"Name": "ISP"
},
{
"Id": "7e0c57046d3f",
"DbName": "Multi Test",
"Name": "MMP"
}]
}
My UI allows the user to input an ID. What i have to do is to loop through the Json value returned from the API and find the one that matches the ID entered. Once i find a match, i have to check if the database has "Test" keyword in it. If it does, i will need to show the DbName and the shortname.
I have found some guide here (e.g. Foreach with JSON Arrays in Play2 and Scala) but it did not work for me. When i run my code, i get this error:
play.api.libs.json.JsResultException: JsResultException(errors:List(((0)/Id,List(ValidationError(List(error.path.missing),WrappedArray()))), ((0)/DbName,List(ValidationError(List(error.path.missing),WrappedArray()))), ((1)/Id,List(ValidationError(List(error.path.missing),WrappedArray()))), ((1)/DbName,List(ValidationError(List(error.path.missing),WrappedArray()))), ((2)/Id,List(ValidationError(List(error.path.missing),WrappedArray()))), ((2)/DbName,List(ValidationError(List(error.path.missing),WrappedArray()))),
Here is my code:
case class DBInfo(Id: String, DbName: String, Name: String)
contentType = "application/json"
//get json from http
val httpClient = HttpClients.createDefault()
val httpResponse = httpClient.execute(new HttpGet("http://www.customers.com/dbInfo"))
val entity = httpResponse.getEntity
val content = fromInputStream(httpResponse.getEntity.getContent()).getLines().mkString
implicit val dbReader = Json.reads[DBInfo]
val dbList = (Json.parse(content) \ "items").as[List[DBInfo]]
dbList.foreach { dbI =>
if (dbI.Id == id)
if (dbI.DbName.contains("Test"))
println(dbI.DbName + " - " + dbI.Name)
else BadRequest("Not allowed")
else
BadRequest("ID not found")
}
id is the variable that holds the inputed ID by the user. Can someone tell me why the error and how to fix it? Thanks.
note: Please using import org.json4s.JsonAST or import play.api.libs.json
already got the answer. so this is how i did it:
case class databaseInfo(Id: String, DbName: String, Name: String)
class dbInfo{
def CheckDb(id: String): Option[String] = {
val httpClient = HttpClients.createDefault()
val httpResponse = httpClient.execute(new HttpGet("http://example.com"))
val content = fromInputStream(httpResponse.getEntity.getContent()).getLines().mkString
val envItems = (parse(content) \\ "items").children
for (items <- envItems) {
val dbItems = items.extract[databaseInfo]
if (dbItems.EnvId == Some(id)) {
if (equalsIgnoreCase(dbItems.DbName.mkString, "Test")) //do something
else //do something
}
}
None
}
}
Here is an approach using circe. You can navigate the JSON with a Cursor, and decode to a list of Environment using the Decoder[A] typeclass. Note that you work with Either[Failure, A] values.
import io.circe._
case class Environment(id: String, dbName: String, name: String)
implicit val environmentDecoder: Decoder[Environment] = Decoder.instance[Environment] {
json =>
for {
id <- json.downField("Id").as[String]
dbName <- json.downField("DbName").as[String]
name <- json.downField("Name").as[String]
} yield {
Environment(id, dbName, name)
}
}
// alternatively:
// implicit val environmentDecoder: Decoder[Environment] =
// Decoder.forProduct3[String, String, String, Environment]("Id", "DbName", "Name")(Environment.apply)
val text =
"""{
| "count": 3,
| "items": [{
| "Id": "fd0a9e5a",
| "DbName": "Xterior Prod",
| "Name": "XP"
| }, {
| "Id": "4158a1a6",
| "DbName": "Invidibi Pappear",
| "Name": "ISP"
| }, {
| "Id": "7e0c57046d3f",
| "DbName": "Multi Match Test",
| "Name": "MMP"
| }]
|}
""".stripMargin
val json = parser.parse(text).fold(_ => ???, json => json)
val res: Either[DecodingFailure, List[Environment]] = json.hcursor.downField("items").as[List[Environment]]
println(res)
// Right(List(Environment(fd0a9e5a,Xterior Prod,XP), Environment(4158a1a6,Invidibi Pappear,ISP), Environment(7e0c57046d3f,Multi Match Test,MMP)))
// or simply
// val res2 = parser.parse(text).right
// .flatMap(_.hcursor.downField("items").as[List[Environment]])
You can also use http4s' http4s-blaze-client and http4s-circe to do HTTP requests:
import org.http4s._
import org.http4s.circe._
import scalaz.concurrent._
val client = org.http4s.client.blaze.defaultClient
val fetchEnvironments: Task[List[Environment]] =
client.fetchAs[Json](Request(Method.GET, Uri.uri("http://example.com")))
.flatMap { json =>
json.hcursor.downField("items").as[List[Environment]].fold(
failure => Task.fail(failure),
xs => Task.now(xs)
)
}
val xs = fetchEnvironments.unsafePerformSync

How to remove List() and escape characters from response JSON in Scala

I have the following function that takes in a JSON input and validates it against a JSON-Schema using the "com.eclipsesource" %% "play-json-schema-validator" % "0.6.2" library. Everything works fine expect for when I get an invalid JSON, I try to collect all violations into a List and later return that list along with the response JSON. However my List is encoded with List() and also has escape characters. I want to have the response JSON look like this:
{
"transactionID": "123",
"status": "error",
"description": "Invalid Request Received",
"violations": ["Wrong type. Expected integer, was string.", "Property action missing"]
}
Instead of this: (This is what I am getting right now)
{
"transactionID": "\"123\"",
"status": "error",
"description": "Invalid Request Received",
"violations": "List(\"Wrong type. Expected integer, was string.\", \"Property action missing\")"
}
And here's the actual function for JSON validation
def validateRequest(json: JsValue): Result = {
{
val logger = LoggerFactory.getLogger("superman")
val jsonSchema = Source.fromFile(play.api.Play.getFile("conf/schema.json")).getLines.mkString
val transactionID = (json \ "transactionID").get
val result: VA[JsValue] = SchemaValidator.validate(Json.fromJson[SchemaType](
Json.parse(jsonSchema.stripMargin)).get, json)
result.fold(
invalid = { errors =>
var violatesList = List[String]()
var invalidError = Map("transactionID" -> transactionID.toString(), "status" -> "error", "description" -> "Invalid Request Received")
for (msg <- (errors.toJson \\ "msgs"))
violatesList = (msg(0).get).toString() :: violatesList
invalidError += ("violations" -> (violatesList.toString()))
//TODO: Make this parsable JSON list
val errorResponse = Json.toJson(invalidError)
logger.error("""Message="Invalid Request Received" for transactionID=""" + transactionID.toString() + "errorResponse:" + errorResponse)
BadRequest(errorResponse)
},
valid = {
post =>
db.writeDocument(json)
val successResponse = Json.obj("transactionID" -> transactionID.toString, "status" -> "OK", "message" -> ("Valid Request Received"))
logger.info("""Message="Valid Request Received" for transactionID=""" + transactionID.toString() + "jsonResponse:" + successResponse)
Ok(successResponse)
}
)
}
}
UPDATE 1
I get this after using Json.obj()
{
"transactionID": "\"123\"",
"status": "error",
"description": "Invalid Request Received",
"violations": [
"\"Wrong type. Expected integer, was string.\"",
"\"Property action missing\""
]
}
I got the escape characters removed by modifying this line:
violatesList = (msg(0).get).toString() :: violatesList
TO:
violatesList = (msg(0).get).as[String] :: violatesList
What you want is a JSON array, but by calling .toString() on your list, you're actually passing a string. Play has an implicit serializer for Lists to JSON arrays, so you actually just have to do less then what you already did - you can just remove the toString() part from violatesList.toString().
In addition, don't create a map for your JSON and then convert it to a JSON, you can use Json.obj with a very similar syntax instead:
val invalidError = Json.obj("transactionID" -> transactionID, "status" -> "error", "description" -> "Invalid Request Received")
for (msg <- (errors.toJson \\ "msgs"))
violatesList = (msg(0).get) :: violatesList
val errorResponse = invalidError ++ Json.obj("violations" -> violatesList)
Regarding your escaped quotes, I suppose it's because transactionID and msgs are JsStrings, so when you convert them with toString() the quotes are included. Just remove the toString everywhere and you'll be fine.

HTTPBuilder, returning JSON in not the correct format

I have a service that I am built\using returning data in the below format.
def responseData = [
'results': results,
'status': results ? "OK" : "Nothing present"
]
render(responseData as JSON)
The output looks like this, I have verified the output according to Fiddler
{"results":[{"class":"com.companyName.srm.ods.territory.Apo","id":2,"apoId":"5T9B0"}],"status":"OK"}
This is a simple POST call with a body of parameters from a search.
Using HTTPBuilder I get a different result
http.request(groovyx.net.http.Method.POST, groovyx.net.http.ContentType.URLENC) {req ->
uri.path = restUrl
body = requestData
response.success = {resp, json ->
println resp.statusLine.statusCode
println resp.statusLine
def slurper = new JsonSlurper()
String s = json.toString()
println s
returnJson = slurper.parseText(s)
}
response."422" = {resp, json ->
println ${resp.statusLine}
}
response.failure = {resp ->
println ${resp.statusLine}
}
}
["results":[{"class":"com.companyName.srm.ods.territory.Apo","id":2,"apoId":"5T9B0"}],"status":"OK":null]
This turns into a Mapped pair where the key is the JSON and the value is null, which is confusing as to why the HTTPBuilder is doing that.
In order to parse to JSON, I have to the following additional coding
s = s.replace(':null]', '')
s = s.replace('[', '')
This seems overly complicated for this type of implementation.
I have turned debug and nothing interesting is coming from that.
Any ideas
I use builder 0.7.1 and get json response the next way:
http.request(Method.POST, ContentType.TEXT) {
uri.path = pathToService
headers.'User' = user
headers.Accept = 'application/json'
body = requestBody //here I post some json
response.success = { resp, reader ->
//println reader.text;
println "response status: ${resp.statusLine}"
return = reader.text
}
response.failure = { resp, reader ->
println "Request failed with status ${resp.status}"
reader.responseData.results.each {
println " ${it.titleNoFormatting} : ${it.visibleUrl}"
}
}
}