Fetch data with getJSONArray in kotlin - json

I'm trying to fetch some data from json. I made function to fetch JSON.
My code:
val obj = JSONObject(getJSONFromAssets()!!)
val userArray = obj.getJSONObject("user_id")
My code to fetchAssets:
private fun getJSONFromAssets(): String ?{
var json: String? = null
val charset: Charset = Charsets.UTF_8
try{
val jsonFile = assets.open(K.json.jsonFileName)
val size = jsonFile.available()
val buffer = ByteArray(size)
jsonFile.read(buffer)
jsonFile.close()
json = String(buffer, charset)
}
catch (ex: IOException){
ex.printStackTrace()
return null
}
return json
}
JSON File:
{
"user_id": "1",
"acounts": [
{
"id": "1",
"AccountNumber": "HR123456789012345678901",
"amount": "2.523,00",
"currency": "USD",
"transactions":
UserModelClass:
data class UserModelClass (
val user_id:String,
val accounts: List<AccountsList>
)
Problem is that I have user ID and then accounts where I can loop easy.
Error: Failed to open file, no such file or directory.
Value 1 at user_id of type java.lang.String cannot be converted to JSONObject

Related

I tried to parse a JSON file on my app but even if there is no error, my app doesn't run on the emulator

The problem might be that the app can't reach the json file I tried to parse it to.No error pops up in the run or build but it stops at"11/23 23:14:38: Launching 'app' on Device 4.Install successfully finished in 325 ms."Below is my code:
MainActivity
class MainActivity : AppCompatActivity() {
#SuppressLint("SuspiciousIndentation", "NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val usersList: ArrayList<UserModelClass> = ArrayList()
try {
val obj = JSONObject(getJSONFromAssets()!!)
val usersArray = obj.getJSONArray("owner")
for (i in 0 until usersArray.length()) {
val user = usersArray.getJSONObject(i)
val login = user.getInt("id")
val name = user.getString("name")
val url = user.getString("url")
val followers_url = user.getString("followers")
val starred_url = user.getString("stars")
val userDetails =
UserModelClass(login, name, url, followers_url , starred_url)
// add the details in the list
usersList.add(userDetails)
}
} catch (e: JSONException) {
//exception
e.printStackTrace()
}
val recyclerView = findViewById<RecyclerView>(R.id.rvUsersList)
val adapter = UserAdapter(this, usersList)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapter
adapter.notifyDataSetChanged()
}
private fun getJSONFromAssets(): String? {
var json: String? = null
val charset: Charset = Charsets.UTF_8
try {
val myUsersJSONFile = assets.open("JSON.json")
val size = myUsersJSONFile.available()
val buffer = ByteArray(size)
myUsersJSONFile.read(buffer)
myUsersJSONFile.close()
json = String(buffer, charset)
} catch (ex: IOException) {
ex.printStackTrace()
return null
}
return json
}
}
UserAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
class UserAdapter( val context: Context, val items: ArrayList<UserModelClass>) :
RecyclerView.Adapter<UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
return UserViewHolder(
LayoutInflater.from(context).inflate(
R.layout.item_user,
parent,
false
)
)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int {
return items.size
}
}
UserViewHolder
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each item to
var tvId : TextView
var tvName : TextView
var tvURL : TextView
var tvFollowers : TextView
var tvStars : TextView
init {
tvId = view.findViewById(R.id.tvId)
tvName = view.findViewById(R.id.tvName)
tvURL = view.findViewById(R.id.tvURL)
tvFollowers = view.findViewById(R.id.tvFollowers)
tvStars = view.findViewById(R.id.tvStars)
}
fun bind(item: UserModelClass) {
tvId.text = item.login.toString()
tvName.text = item.node_id
tvURL.text = item.avatar_url
tvFollowers.text = item.followers_url.toString()
tvStars.text = item.starred_url.toString()
}
}
UserModelClass
class UserModelClass(
val login: Int,
val node_id: String,
val avatar_url: String,
val followers_url: String,
val starred_url: String
)
Json.json
I don't need all the information from the json, I selected some of them
{
"total_count": 357602,
"incomplete_results": false,
"items": [
{
"id": 23096959,
"node_id": "...",
"name": "...",
"full_name": "...",
"private": false,
"owner": {
"login": "...",
"id": 4314092,
"node_id": "...",
"avatar_url": "https://...",
"url": "https://api.github.com/users/golang",
"followers_url": "https://api.github.com/users/golang/followers",
"following_url": "https://api.github.com/users/golang/following{/other_user}",
"starred_url": "https://api.github.com/users/golang/starred{/owner}{/repo}",
},
Actually it seems you are parsing it wrong. In your json, items is the array and owner is an object inside items array item.
You cannot access owner directly. Also you should give exact keys.
Like "followers_url" not just "followers"
Try the below code.
try {
val obj = JSONObject(getJSONFromAssets()!!)
val itemsArray = obj.getJSONArray("items")
for (i in 0 until itemsArray.length()) {
val user = itemsArray.getJSONObject(i)
val name = user.getString("name") // Getting name from item object
val owner = user.getJSONObject("owner") //Getting owner object from item object
//And below you need to get items from owner object not the user object.
val login = owner.getInt("id")
val url = owner.getString("url")
val followers_url = owner.getString("followers_url")
val starred_url = owner.getString("starred_url")
val userDetails =
UserModelClass(login, name, url, followers_url , starred_url)
// add the details in the list
usersList.add(userDetails)
}
} catch (e: JSONException) {
//exception
e.printStackTrace()
}

How to use Gson to deserialize a json array string to object mode using generic class type?

Having a function for deserializing json string into object mode by providing the class type the json string should be deserialized into.
It works for most of the case
fun <T> deserializeJsonStr(jsonString: String, dataClassType: Class<T>):T? {
var ret: T? = Gson().fromJson<T>(jsonString, dataClassType)
return ret
}
like
// data modle
class TestData1 {
var userId = 0
var id = 0
var title: String? = null
var body: String? = null
}
// the json string
{
"userId": 1,
"id": 3,
"title": "the title",
"body": "the body"
}
// call the function
val clazz: Class<TestData1> = TestData1::class.java
val theTestData1 = deserializeJsonStr(jsonString, clazz)
it will return the object theTestData1 with the fields filled.
But if the json string is for json array:
[
{
"userId": 1,
"id": 1,
"title": "title1",
"body": "body1"
},
{
"userId": 1,
"id": 2,
"title": "title2",
"body": "body2"
},
{
"userId": 1,
"id": 3,
"title": "title3",
"body": "body3"
}
]
Though it should be ArrayList<TestData1>, but what is it class type? tried:
val clazz: Class<ArrayList<TestData1>> = ArrayList<TestData1>::class.java
val theTestData1 = psreJsonStr(jsonString, clazz)
but it does not compile:
or the val pojoClass: Class<ArrayList<TestData1>> = ArrayList<>::class.java does not compile either:
what would be the data class mode for this json array? or what is the Class<T> required by the function param for ArrayList?
This is related to type erasure: ArrayList<TestData1>::class.java is not possible because at runtime the type argument of ArrayList is not available, so this would basically be Class<ArrayList>1. Therefore the compiler does not allow this because it would not be safe.
Gson has its TypeToken class to work around this, see also the user guide.
In your case you could change your deserializeJsonStr to the following:
fun <T> deserializeJsonStr(jsonString: String, dataType: TypeToken<T>): T? {
// Note: Older Gson versions might require using `dataType.type`
return Gson().fromJson(jsonString, dataType)
}
You would then call the function like this:
val type = object: TypeToken<ArrayList<TestData1>>() {}
val data = deserializeJsonStr(jsonString, type)
You could additionally add an overload of deserializeJsonStr with reified type parameter which makes it make more convenient to use:
inline fun <reified T> deserializeJsonStr(jsonString: String): T? {
return deserializeJsonStr(jsonString, object: TypeToken<T>() {})
}
// Note: You can even omit the explicit `ArrayList<TestData1>` if the compiler can infer it
val data = deserializeJsonStr<ArrayList<TestData1>>(jsonString, type)
1 Class is a bit special because its direct type argument is not erased at runtime, for example Class<ArrayList> is not erased to Class<Object>. But for every other generic type the type argument is erased.

How to get a nested specific json property with Kotlinx serialization in Ktor Kotlin without a data class?

I'm writing an web app with Kotlin and Ktor and I want to parse the json response from Google Books Api and select some specific values from it to store.
For serialization I am using Kotlinx serialization.
I want to avoid creating a data class
The json response from the api is nested and looks like this (Json has been shortened for clarity):
{
"kind": "books#volumes",
"totalItems": 795,
"items": [{
"kind": "books#volume",
"id": "zYw3sYFtz9kC",
"etag": "ylNs2Stlgis",
"selfLink": "https://www.googleapis.com/books/v1/volumes/zYw3sYFtz9kC",
"volumeInfo": {
"title": "The Contemporary Thesaurus of Search Terms and Synonyms",
"subtitle": "A Guide for Natural Language Computer Searching",
"authors": [
"Sara D. Knapp"
],
"publisher": "Greenwood Publishing Group",
"publishedDate": "2000"
}
}]
}
So how can I access the title property in this json with Kotlin / Kotlinx? I get the json object in items, but couldn't go until title.
That's my code so far after calling the api.
Routing.kt:
val response: HttpResponse = client.get("https://www.googleapis.com/books/v1/volumes?q=isbn:$isbn")
println(response.status)
val stringBody: String = response.body()
val json: Map<String, JsonElement> = Json.parseToJsonElement(stringBody).jsonObject
//println(json)
val item = json["items"]
//println(item)
You can cast JsonElement objects to more concrete types to retrieve necessary information:
val obj = Json.parseToJsonElement(stringBody) as JsonObject
val items = obj["items"] as JsonArray
for (item in items) {
val info = (item as JsonObject)["volumeInfo"]
val title = (info as JsonObject)["title"].toString()
println(title)
}
I solved it on this way. So I get the title of the book in variable "title":
val stringBody: String = response.body()
val json: Map<String, JsonElement> = Json.parseToJsonElement(stringBody).jsonObject
val items = json["items"]!!.jsonArray
val item = items[0]
val itemInfo = item.jsonObject
val volumeInfo = itemInfo["volumeInfo"]
val volumeInfoObject = volumeInfo?.jsonObject
val title = volumeInfoObject?.get("title")
println("d: $title")

Parse JSON and convert object back to JSON

I want to parse a nested JSON structure in Groovy. I would like to parse a sub element structure and then return the string in JSON format.
The Nested JSON structure:
{
"username": "test",
"token": "test1",
"url": "http://www.abc.to",
"testsession":
{
"serverName": "0.0.0.0",
"serverPort": 22,
"remoteUsername": "admin",
"remotePassword": "admin"
},
"deviceapp":
{
"repo": "abc-mvn-a-test-local",
"path": "com/test\/test2\/test3\/mob",
"platform": "ANDROID"
}
}
my code below using JSONSlurper isn't quite giving me what i want:
def slurper = new JsonSlurper().parseText(json)
String deviceAppParsed = slurper.deviceapp
println "deviceAppParsed " + deviceAppParsed
// returns deviceAppParsed {repo=oxp-mvn-a-rel-local, path=com/nagra/opentv/experience/mob, platform=ANDROID}
def jsonDeviceApp = JsonOutput.toJson(deviceAppParsed)
println "IS IT JSON? " + jsonDeviceApp
// returns IS IT JSON "{repo=oxp-mvn-a-rel-local, path=com/nagra/opentv/experience/mob, platform=ANDROID}"
How can i parse the json to retrieve the nested deviceapp structure in raw JSON? Thanks.
:
def slurper = new JsonSlurper().parseText(json)
String deviceAppParsed = slurper.deviceapp
def jsonDeviceApp = JsonOutput.toJson(deviceAppParsed)
I expected println jsonDeviceApp to return:
{"repo": "abc-mvn-a-test-local","path": "com/test\/test2\/test3\/mob","platform": "ANDROID"}
instead it returned:
"{repo=oxp-mvn-a-rel-local, path=com/nagra/opentv/experience/mob, platform=ANDROID}"
just replace String to def in the following line:
String deviceAppParsed = slurper.deviceapp
by using string you are converting Object returned by slurper.deviceapp to string
should be:
def deviceAppParsed = slurper.deviceapp
in this case last line will print json
{"repo":"abc-mvn-a-test-local","path":"com/test/test2/test3/mob","platform":"ANDROID"}

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