Android Kotlin Volley How to get value from JSONArray - android

I want to get "file" in "media_gallery_entries" JSONArray by Volley for loop
Volley
val item = ArrayList<RecyData>()
val jsonRequest = object : JsonObjectRequest(Request.Method.GET, url, null,
Response.Listener { response ->
try {
val jsonArrayItems = response.getJSONArray("items")
val jsonSize = jsonArrayItems.length()
for (i in 0 until jsonSize) {
val jsonObjectItems = jsonArrayItems.getJSONObject(i)
val pName = jsonObjectItems.getString("name")
val pPrice = jsonObjectItems.getInt("price")
item.add(RecyData(pName, pPrice, pImage))
}
} catch (e: JSONException) {
e.printStackTrace()
}
Data
{
"items": [
{
"id": 1,
"sku": "10-1001",
"name": "item01",
"price": 100,
"media_gallery_entries": [
{
"id": 1,
"file": "//1/0/10-28117_1_1.jpg"
}
]
}
]
}

Instead of doing manual JSON parsing which is always error prone, you can use some parsing library such as Gson which is well tested and very less likely to cause any issue
To use Gson first you need to add dependency in your build.gradle as
implementation 'com.google.code.gson:gson:2.8.7'
Now define kotlin types which maps to your JSON response
class Media(
val id: Int,
val file: String
)
class Entry(
val id: Int,
val sku: String,
val name: String,
val price: Int,
val media_gallery_entries: List<Media>
)
Now in response listener just do
try{
val jsonArrayItems = response.getJSONArray("items")
val token = TypeToken.getParameterized(ArrayList::class.java, Entry::class.java).type
val result:List<Entry> = Gson().fromJson(jsonArrayItems.toString(), token)
// Do something with result
}
catch (e: JSONException) {
e.printStackTrace()
}

Try this code after val pPrice = jsonObjectItems.getInt("price") this line
val mediaEntryArr = jsonObjectItems.getJSONArray("media_gallery_entries")
for(j in 0 until mediaEntryArr.length()){
val mediaEntryObj = mediaEntryArr.getJSONObject(j)
val id = mediaEntryObj.getString("id")
val file = mediaEntryObj.getString("file")
Log.e("mediaEntry----",""+ Gson().toJson(mediaEntryObj))
Log.e("id----",""+ id)
Log.e("file----",""+ file)
}

Related

Json array is being converted to string when sending data to api. how to remove it?

"[[{"NameOfNote":"P","Height":"0","Alteration":"100","Frequency":"0.0","Finger":"0","String":"0","Time":"16","Dotted_Note":"0","ToBeDisplayed":"true"},{"NameOfNote":"P","Height":"0","Alteration":"100","Frequency":"0.0","Finger":"0","String":"0","Time":"16","Dotted_Note":"0","ToBeDisplayed":"true"}],[{"NameOfNote":"P","Height":"0","Alteration":"100","Frequency":"0.0","Finger":"0","String":"0","Time":"16","Dotted_Note":"0","ToBeDisplayed":"true"},{"NameOfNote":"P","Height":"0","Alteration":"100","Frequency":"0.0","Finger":"0","String":"0","Time":"16","Dotted_Note":"0","ToBeDisplayed":"true"}]]"
Above is my output
Below is my implementation
var mainJson = JSONArray()
for(i in 0..song_result.size-1){
var innerJSONArray = JSONArray()
for (j in 0..song_result[i].size -1){
var obj = JSONObject()
try {
obj.put("NameOfNote", song_result[i][j].NameOfNote)
obj.put("Height", song_result[i][j].Height)
obj.put("Alteration", song_result[i][j].Alteration)
obj.put("Frequency", song_result[i][j].Frequency)
obj.put("Finger", song_result[i][j].Finger)
obj.put("String", song_result[i][j].String)
obj.put("Time", song_result[i][j].Time)
obj.put("Dotted_Note", song_result[i][j].Dotted_Note)
obj.put("ToBeDisplayed", song_result[i][j].ToBeDisplayed)
} catch (e: JSONException) {
e.printStackTrace()
}
innerJSONArray.put(obj)
}
mainJson.put(innerJSONArray)
}
json is not encoded well let me solve your problem
add the gson lib from here
add this class
data class Root (
#SerializedName("NameOfNote") val nameOfNote : String,
#SerializedName("Height") val height : Int,
#SerializedName("Alteration") val alteration : Int,
#SerializedName("Frequency") val frequency : Double,
#SerializedName("Finger") val finger : Int,
#SerializedName("String") val string : Int,
#SerializedName("Time") val time : Int,
#SerializedName("Dotted_Note") val dotted_Note : Int,
#SerializedName("ToBeDisplayed") val toBeDisplayed : Boolean
)
Call this method
val obj= Root(nameOfNote = "name",height = 9,alteration = 7,frequency = 7.8,finger = 7,string = 7,time = 8,dotted_Note = 9,toBeDisplayed = false)
val json= Gson ().toJson(obj)
You can do this in for loop just rember to add in string dont replace

Android: JSON object getString

I use this JSON https://api.github.com/users
I need to get string name, followers, following, and more. But on the program says "No value for name". I think I need to go to a specific user example: https://api.github.com/users/mojombo to getting that info, but I don't know-how.
And I using loopj library.
Here's My Code
private fun getDataGitDetail() {
progressBar.visibility = View.VISIBLE
val client = AsyncHttpClient()
client.addHeader("Authorization", "token 6fe9dff2e5e43d25eb3abe9ff508a750b972f725")
client.addHeader("User-Agent", "request")
val url = "https://api.github.com/users"
client.get(url, object : AsyncHttpResponseHandler() {
override fun onSuccess(
statusCode: Int,
headers: Array<Header>,
responseBody: ByteArray
) {
progressBar.visibility = View.INVISIBLE
val result = String(responseBody)
Log.d(TAG, result)
try {
val jsonArray = JSONArray(result)
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
val username: String? = jsonObject.getString("login")
val name: String? = jsonObject.getString("name")
val avatar: String? = jsonObject.getString("avatar_url")
val company: String? = jsonObject.getString("url")
val location: String? = jsonObject.getString("url")
val repository: Int = 0
val followers: Int = 0
val following: Int = 0
listData.add(
Data(
username,
name,
avatar,
company,
location,
repository,
followers,
following
)
)
}
showRecyclerList()
} catch (e: Exception) {
Toast.makeText(this#MainActivity, e.message, Toast.LENGTH_SHORT)
.show()
e.printStackTrace()
}
}
override fun onFailure(
statusCode: Int,
headers: Array<Header>,
responseBody: ByteArray,
error: Throwable
) {
progressBar.visibility = View.INVISIBLE
val errorMessage = when (statusCode) {
401 -> "$statusCode : Bad Request"
403 -> "$statusCode : Forbidden"
404 -> "$statusCode : Not Found"
else -> "$statusCode : ${error.message}"
}
Toast.makeText(this#MainActivity, errorMessage, Toast.LENGTH_LONG)
.show()
}
})
}
The current response you are getting does not contain a key name in the JSONObject.
If you want the Name of all the users you will have to go to each users endpoint in the api. You'll need to make another request inside your for loop that gets datafrom an endpoint like https://api.github.com/users/mojombo
val jsonArray = JSONArray(result)
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
val username: String? = jsonObject.getString("login")
//Make the request here using "https://api.github.com/users/" + login
You can then choose to get the rest of the data from either the first response or the 2nd one as both contain that information.
I hope this helps.
No need for a JSON array, cz API https://api.github.com/users/mojombo is JSON Object.
Example:
client.get(url, object : AsyncHttpResponseHandler() {
override fun onSuccess(statusCode: Int, headers: Array<Header>, responseBody: ByteArray) {
try {
//parsing json
val result = String(responseBody)
val responseObject = JSONObject(result)
textView2.text = responseObject.getString("login")
textView3.text = responseObject.getString("name")
textView9.text = responseObject.getString("location")
desc.text = responseObject.getString("company")
view?.let { Glide.with(it).load(responseObject.getString("avatar_url")).into(imageView2) }
} catch (e: Exception) {
Log.d("Exception", e.message.toString())
}
}
}

Parse internal map from JSON in Moshi

I am trying to parse following JSON using moshi but I am unable to dynamic data like USA or UK. USA and UK are dynamic keys.
{
"USA": {
"name": "United State of America",
"code": "US"
},
"UK": {
"name": "United Kingdom",
"code": "UK"
},
"soft_update": "500",
"hard_update": "500"
}
Data class:
data class ApiAppUpdate(val countryMap: Map<String, ApiCountry>,
#field:Json(name = "hard_update")
val forceUpdateVersion: Int,
#field:Json(name = "soft_update")
val softUpdateVersion: Int)
Following is my json adapter code:
fun getConfig(){
val adapter: JsonAdapter<ApiAppUpdate> = moshi.adapter(ApiAppUpdatee::class.java)
}
I get soft and hard update values but countryMap is always null. I am not sure what's wrong in here. Can someone please help me. Thanks.
The problem is that your model description would match this json and not the one you attached:
{
"countryMap":{
"USA":{
"name":"United State of America",
"code":"US"
},
"UK":{
"name":"United Kingdom",
"code":"UK"
}
},
"soft_update":"500",
"hard_update":"500"
}
In your example "USA" and "UK" are on the same level with "soft_update" and "hard_update".
UPDATE:
This is a working solution:
data class ApiCountry(val name: String, val code: String)
data class ApiAppUpdate(
val countryMap: Map<String, ApiCountry>,
#field:Json(name = "hard_update")
val forceUpdateVersion: Int,
#field:Json(name = "soft_update")
val softUpdateVersion: Int
)
class ApiUpdateAdapter {
#FromJson
fun fromJson(reader: JsonReader): ApiAppUpdate {
var forceUpdateVersion: Int = -1
var softUpdateVersion: Int = -1
val map: MutableMap<String, ApiCountry> = mutableMapOf()
reader.beginObject()
while (reader.hasNext()) {
when (reader.peek()) {
JsonReader.Token.NAME ->
when (val fieldName = reader.nextName()) {
"hard_update" -> forceUpdateVersion = reader.nextInt()
"soft_update" -> softUpdateVersion = reader.nextInt()
else -> {
reader.beginObject()
var name = ""
var code = ""
while (reader.hasNext()) {
when (reader.nextName()) {
"name" -> name = reader.nextString()
"code" -> code = reader.nextString()
else -> reader.skipValue()
}
}
reader.endObject()
map[fieldName] = ApiCountry(name, code)
}
}
else -> reader.skipValue()
}
}
reader.endObject()
return ApiAppUpdate(map, forceUpdateVersion, softUpdateVersion)
}
}
fun main() {
val sourceJson =
"{\"USA\":{\"name\":\"United States of America\",\"code\":\"US\"},\"UK\":{\"name\":\"United Kingdom\",\"code\":\"UK\"}" +
",\"soft_update\":\"200\",\"hard_update\":\"300\"}"
val adapter = Moshi.Builder()
.add(ApiUpdateAdapter())
.build()
.adapter(ApiAppUpdate::class.java)
val apiAppUpdate = adapter.fromJson(sourceJson)
println(apiAppUpdate)
}

Gson deserialization for a list in Kotlin

This is what my json looks like
{
"sub": "9",
"auth_time": 1559381757,
"idp": "idsrv",
"role": [
"Employer",
"Employee",
"Student"
],
"iss": "",
"aud": "",
"exp": 1574933757,
"nbf": 1559381757
}
This is the object I want to convert this Json into.
data class Claims (
#SerializedName("nameid") val nameId: String,
#SerializedName("unique_id") val uniqueId: String,
#SerializedName("sub") val sub: String,
#SerializedName("unifiedNumber") val unifiedNumber: String,
#SerializedName("role") var roleList: List<Role>
)
I wrote a custom Deserializer (which works in Java) for the List type
class RoleDeserializer : JsonDeserializer<List<Role>> {
private var roleId = 0
#Throws(JsonParseException::class)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): MutableList<Role> {
val resultList = ArrayList<Role>()
if (json.isJsonArray) {
for (e in json.asJsonArray) {
resultList.add(Role(id = roleId++, name = e.asString))
}
} else if (json.isJsonObject) {
resultList.add(Role(id = roleId++, name = json.asString))
} else if (json.isJsonPrimitive) {
if ((json as JsonPrimitive).isString)
resultList.add(Role(id = roleId++, name = json.getAsString()))
} else {
throw RuntimeException("Unexpected JSON type: " + json.javaClass)
}
return resultList
}
}
This is how I register my type adapter
val listType: Type = object : TypeToken<List<Role>>() {}.type
val gson = GsonBuilder().registerTypeAdapter(listType, RoleDeserializer()).create()
val claims = gson.fromJson(stringJson, Claims::class.java)
I still get a parse exception stating that
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 161 path $.role[0]
and my RoleDeserializer is never called. Am I doing something wrong while registering the type adapter?
Try to replace
val listType: Type = object : TypeToken<List<Role>>() {}.type
with
val listType: Type = object : TypeToken<MutableList<Role>>() {}.type
The role is String array in JSON
Use this
#SerializedName("role") var roleList: List<String>
Instead of this
#SerializedName("role") var roleList: List<Role>
Try this
data class Claims (
#SerializedName("nameid") val nameId: String,
#SerializedName("unique_id") val uniqueId: String,
#SerializedName("sub") val sub: String,
#SerializedName("unifiedNumber") val unifiedNumber: String,
#SerializedName("role") var roleList: List<String>
)

How to properly make reusable class to catch response data using retrofit?

I am new in Android development, and I am trying to get data from server. the general JSON response structure will be like this
{
"success": "1",
"data": [
{
"customers_id": 4,
"customers_gender": "0",
"customers_firstname": "TES IOS",
"customers_lastname": "TES IOS",
"customers_dob": "2018-12-27",
"email": "TES002#email.com",
"user_name": "TES002",
"customers_default_address_id": 0,
"customers_telephone
},
"message": "Successfully get user data from server"
}
the "success" and "message" field will be the same (will always be string). but the "data" can be different for other request call. It can send user data, store data or product data, or even Array/List of Products.
so I want to make general reusable class to catch that JSON response. the class will be like this, I set the "data" to be Any, and then later it will be casted back to User object:
class ServerData(successStatus: Int, data: Any, message: String) {
val isSuccessfull : Boolean
val data : Any
val message : String
init {
isSuccessfull = successStatus != 0
this.data = data
this.message = message
}
}
the interface is like this:
interface LakuinAPI {
#FormUrlEncoded
#POST("processlogin")
fun performLogin(
#Field("kode_customer") outletCode: String,
#Field("password") password: String
): Call<ServerData>
}
and then I use it in the activity, like the code below:
private fun sendLoginDataToServer(outletCode: String, password: String) {
val call = lakuinAPI.performLogin(outletCode,password)
call.enqueue(object: Callback<ServerData> {
override fun onFailure(call: Call<ServerData>, t: Throwable) {
Toast.makeText(this#LoginActivity,t.localizedMessage,Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<ServerData>, response: Response<ServerData>) {
if (!response.isSuccessful) {
Toast.makeText(this#LoginActivity,"Code: " + response.code(),Toast.LENGTH_LONG).show()
return
}
val lakuinServerData = response.body()
val userList = lakuinServerData?.data as List<User> // the error in here
val userData = userList.first() // the error in here
println(userData)
}
})
}
but I get error message:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap
cannot be cast to com.ssss.lakuinkotlin.Model.User
I give comment in the code above the location of the error. I don't why it happened.
to be honest, I am not if this is the correct way to catch user data from general response JSON like the the JSON above. is there a better way ?
You can use generics to achieve it
class Response<Data> constructor() : ResponseSimple() {
#SerializedName(FIELD_DATA)
var data: Data? = null
private constructor(data: Data) : this() {
this.data = data
}
companion object {
const val FIELD_SUCCESS = "success"
const val FIELD_ERROR = "error"
const val FIELD_DATA = "data"
const val FIELD_MESSAGE = "message"
#JvmStatic
fun <Data> create(data: Data): Response<Data> {
return Response(data)
}
}
}
And ResponseSimple is
open class ResponseSimple {
#SerializedName(Response.FIELD_ERROR)
var error: String = ""
#SerializedName(Response.FIELD_SUCCESS)
var succes: Boolean = false
#SerializedName(Response.FIELD_MESSAGE)
var message:String = ""
}
Then api response should be Call<Response<ServerData>>.
And about ClassCastException, you can't convert ServerData to User just using as.
You need to use Call<Response<ArrayList<User>>> or create class converter.
Try replacing this line :
val userList = lakuinServerData?.data as List<User>
with:
val userList = lakuinServerData?.data as new TypeToken<List<User>>(){}.getType()

Categories

Resources