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())
}
}
}
Related
I have a token like this:
hereeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcGVyYWRvciI6eyJpZCI6NzAsIm5vbWUiOiJERUlWSVRJIiwidXN1YXJpbyI6IkRFSVZJVEkifSwiaWF0IjoxNjI5ODEyNDA1fQ.JqzQnFSbG6gFsnlJu3-bezxZ_N5e5FEzc9QvpRGu0u4
hide it:
alg: "HS256",
typ: "JWT"
}.
operador: {
id: 20,
nome: "JOAO",
usuario: "JOAO"
},
iat: 1629812405
}
Question is how do I get on android kotlin only user id to use in certain tasks?
You could use this,
https://github.com/auth0/JWTDecode.Android
Assuming the iat value is the user id,
var jwt: JWT = JWT(YOUR_TOKEN_STRING)
var claim: Claim = jwt.getClaim("iat")
//or as a string
var claim: String = jwt.getClaim("iat").asString()
I just fix the issue thanks to this:
private fun decodeToken(jwt: String): String {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return "Requires SDK 26"
val parts = jwt.split(".")
return try {
val charset = charset("UTF-8")
val header = String(Base64.getUrlDecoder().decode(parts[0].toByteArray(charset)), charset)
val payload = String(Base64.getUrlDecoder().decode(parts[1].toByteArray(charset)), charset)
"$header"
"$payload"
} catch (e: Exception) {
"Error parsing JWT: $e"
}
}
Then :
val mDecode = decodeToken(mToken)
val test = JSONObject(mDecode).getString("operador")
val mDecodeTokenOk = JSONObject(test).getString("id")
/** SALVANDO ID_OPERADOR */
mSharedPreferences.saveString(WmsConstantes.ID_OPERADOR,mDecodeTokenOk)
Log.e("------------------>", mDecodeTokenOk.toString());
You don't have to install any libraries. You can try something like this.
Class(s) reflecting your JWT payload
data class JwtPayload(
#SerializedName("iat")
val iat: Int,
#SerializedName("operador")
val operador: Operador
)
data class Operador(
#SerializedName("id")
val id: Int,
#SerializedName("nome")
val nome: String,
#SerializedName("usuario")
val usuario: String
)
You can use this class as a wrapper for your token
class Jwt(private val token: String) {
private val userData: JsonObject by lazy {
val userData = String(Base64.decode(token.split(".")[1], Base64.DEFAULT), StandardCharsets.UTF_8)
JsonParser.parseString(userData).asJsonObject
}
fun getUserData(): JwtPayload{
gson.toJson(userData, Jwt::class.java)
return gson.fromJson(userData, JwtPayload::class.java)
}
fun isExpired(): Boolean {
return userData.asJsonObject.get("exp").asLong < (System.currentTimeMillis() / 1000)
}
companion object {
#JvmStatic
private val gson = Gson()
}
}
Usage
val token = Jwt("YOUR_TOKEN")
val operatorID = token.operator.id
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)
}
I am using Single.zip() and Function 5 to consolidate 5 API calls into one Single:
private fun loadProfileAndBalances() {
registerSubscription(
Single.zip<AvailableFundsResult, IncomingFundsResult, TotalEarnedResult, TotalDonatedResult, ProfileResult, Unit>(
Interactors.api.paymentsApiClient.getAvailableFunds()
.map<AvailableFundsResult> {
Timber.d("Available Result [${it.amount}]") <------ DollarAmount object with null amount
AvailableFundsResult.Amount(it) }
.onErrorReturn { AvailableFundsResult.Error(it) }
.scheduleIOUI(),
Interactors.api.paymentsApiClient.getIncomingFunds()
.map<IncomingFundsResult> { IncomingFundsResult.Amount(it) }
.onErrorReturn { IncomingFundsResult.Error(it) }
.scheduleIOUI(),
Interactors.api.paymentsApiClient.getTotalEarned()
.map<TotalEarnedResult> { TotalEarnedResult.Amount(it) }
.onErrorReturn { TotalEarnedResult.Error(it) }
.scheduleIOUI(),
Interactors.api.paymentsApiClient.getTotalDonated()
.map<TotalDonatedResult> { TotalDonatedResult.Amount(it) }
.onErrorReturn { TotalDonatedResult.Error(it) }
.scheduleIOUI(),
Interactors.profileManager.getNotCachedProfile()
.map<ProfileResult> { ProfileResult.Profile(it) }
.onErrorReturn { ProfileResult.Error(it) }
.scheduleIOUI(),
Function5 { availableFunds: AvailableFundsResult, incomingFunds: IncomingFundsResult, totalEarned: TotalEarnedResult, totalDonated: TotalDonatedResult, profileResult: ProfileResult ->
availableTotal = when (availableFunds) {
is AvailableFundsResult.Amount ->
availableFunds.result.amount
is AvailableFundsResult.Error -> {
Timber.w(availableFunds.throwable, "Error while fetching sponsorships")
"0.00"
}
}
incomingTotal = when (incomingFunds) {
is IncomingFundsResult.Amount -> incomingFunds.result.amount
is IncomingFundsResult.Error -> {
Toast.makeText(activity, getString(R.string.payout_main_error_loading_totals), Toast.LENGTH_SHORT).show()
"0.00"
}
}
earnedTotal = when (totalEarned) {
is TotalEarnedResult.Amount -> totalEarned.result.amount
is TotalEarnedResult.Error -> {
Toast.makeText(activity, getString(R.string.payout_main_error_loading_totals), Toast.LENGTH_SHORT).show()
"0.00"
}
}
donatedTotal = when (totalDonated) {
is TotalDonatedResult.Amount -> totalDonated.result.amount
is TotalDonatedResult.Error -> {
Toast.makeText(activity, getString(R.string.payout_main_error_loading_totals), Toast.LENGTH_SHORT).show()
"0.00"
}
}
onboardingComplete = when (profileResult) {
is ProfileResult.Profile ->
profileResult.result.isOnboardingCompleted
is ProfileResult.Error -> {
Timber.e(profileResult.throwable, "Error fetching profile")
true
}
}
}
).ignoreElement()
.subscribe(::updateViews) {
it.printStackTrace()
availableTotal = ""
incomingTotal = ""
earnedTotal = ""
donatedTotal = ""
onboardingComplete= false
vWalletRefresher.isRefreshing = false
internetConnectionError(it)
})
}
Each of these API calls is succeeding with code 200. The call made with Interactors.api.paymentsApiClient.getAvailableFunds() is returning {"amount":264.69}, which is parsed into an object of this class:
internal data class DollarAmount(#SerializedName("amount") val amount: String)
The paymentsApiClient repeatedly referenced is built like this:
private fun createNewPaymentsClient(authRefreshClient: AuthRefreshClient,
preferencesInteractor: PreferencesInteractor): PaymentsApiClient {
val loggingInterceptor = run {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.apply {
httpLoggingInterceptor.level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
}
}
val okHttpClient = createHttpClientBuilder()
.addInterceptor(createSessionRequestInterceptor())
.addInterceptor(createUserAgentInterceptor(context))
.addInterceptor(loggingInterceptor)
.authenticator(RefreshUserAuthenticator(authRefreshClient, preferencesInteractor,
UnauthorizedNavigator(SDKInternal.appContext, Interactors.preferences)))
.build()
val gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().setLenient().create()
return Retrofit.Builder()
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(Interactors.apiEndpoint)
.build()
.create(PaymentsApiClient::class.java)
}
Despite the API call being successful, the log statement that is printed in Single.zip for a successful retrieval of a DollarAmount via Interactors.api.paymentsApiClient.getAvailableFunds() the is showing its amount as being null, rather than 264.69. What is wrong with my parsing that is making this null?
Edit: As someone noticed, I am wrapping the results in another set of classes:
private sealed class AvailableFundsResult {
data class Error(val throwable: Throwable) : AvailableFundsResult()
data class Amount(val result: DollarAmount) : AvailableFundsResult()
}
private sealed class IncomingFundsResult {
data class Error(val throwable: Throwable) : IncomingFundsResult()
data class Amount(val result: DollarAmount) : IncomingFundsResult()
}
private sealed class TotalEarnedResult {
data class Error(val throwable: Throwable) : TotalEarnedResult()
data class Amount(val result: DollarAmount) : TotalEarnedResult()
}
private sealed class TotalDonatedResult {
data class Error(val throwable: Throwable) : TotalDonatedResult()
data class Amount(val result: DollarAmount) : TotalDonatedResult()
}
private sealed class ProfileResult {
data class Error(val throwable: Throwable) : ProfileResult()
data class Profile(val result: InfluencerProfileDto) : ProfileResult()
}
I don't think this is relevant though because it the amount is coming back null before I even spit out an instance of these wrapper classes.
Edit 2: Class that gets returned from 5th API call:
#JsonClass(generateAdapter = true)
internal data class InfluencerProfileDto(
#Json(name = "id") val id: String,
#Json(name = "emailAddress") val email: String?,
#Json(name = "phoneNumber") val phoneNumber: PhoneNumberDto?,
#Json(name = "isPhoneNumberVerified") val isPhoneNumberVerified: Boolean,
#Json(name = "isEmailVerified") val isEmailVerified: Boolean,
#Json(name = "notificationTimePreference") val notificationTimePreference: String,
#Json(name = "isInstagramConnected") val isInstagramConnected: Boolean,
#Json(name = "isFacebookConnected") val isFacebookConnected: Boolean,
#Json(name = "isTwitterConnected") val isTwitterConnected: Boolean,
#Json(name = "currencyIsoSymbol") val currencyIsoSymbol: String,
#Json(name = "currencySymbol") val currency: String,
#Json(name = "birthDate") val birthDate: Date?,
#Json(name = "name") val name: String?,
#Json(name = "profilePictureUri") val avatarUrl: String?,
#Json(name = "gender") val gender: Gender?)
{
val isOnboardingCompleted: Boolean
get() = gender!= Gender.UNKNOWN && birthDate!= null && !!notificationTimePreference.isNullOrEmpty() && isPhoneNumberVerified && !email.isNullOrEmpty()
}
my approach is in java
first of all we need two libraries that you might not be using in your project ie.
gson => for pasing objects from string and vice versa
volley => for string http requests
please google their official github pages and install the latest versions
then we ll be creating a common result object to bear results
class Result {
boolean error; // this field can be used by the api to tell if there was some error while processing the request
String message; // here api can give you some extra message if there was some error
String result; // here you get the result. you can make it a map as well if you want to receive an object and later parse to a unique object class
}
this is a function you can use for HTTP requets
public static Single<Result> getHttpResult(String api, Map<Stirng, String> params){
return Single.create(emitter -> {
StringRequest stringRequest = new StringRequest(Request.Method.POST, api, response -> {
Log.i("RESPONSE :", response);
try {
Result result = new Gson().fromJson(response, Result.java);
emitter.onSuccess(result);
} catch (JSONException e) {
emitter.onError(e);
}
}, emitter::onError) {
#Override
protected Map<String, String> getParams() {
return params;
}
};
stringRequest.setRetryPolicy(new DefaultRetryPolicy(0, 0, 0));
RequestQueue requestQueue = Volley.newRequestQueue(context);
requestQueue.add(stringRequest);
});
}
this is how you can get multiple results at once and i recommend you also check the internet connectivity if user has enough internet then only make parallel requests else it wont produce good UX
...
List<Single<Result>> tasks = new ArrayList();
tasks.add(getHttpResult("https://..."), /**here you can add a map with params that your api might require for auth and other purposes**/);
tasks.add(...);
tasks.add(...);
tasks.add(...);
Single.zip(tasks, objects -> {
List<Result> results = new ArrayList();
for(Object object : objects){
results.add((Result)object)
}
return results;
}).subscribe(results -> {
// this result object have all the results of http requests
// for distinguising results from each other you can create a field on the result object
});
...
hope my approach is helpful to you ;)
I'm new in android dev and I have an issue with my code. I'm trying to parse data from a json, put the information into an object and then return this from a function.
My code:
data class AccountInfo (
var username: String,
var bio: String,
var avatar: String,
var cover: String,
var created: Long,
var reputation: Double
)
fun getAccountInfo(accessToken: String): AccountInfo {
var info = AccountInfo("username", "", "", "", 0, 0.0)
val jsonObjectRequest: JsonObjectRequest = object : JsonObjectRequest(Method.GET, "https://api.imgur.com/3/account/me", null, Response.Listener { response ->
Log.e(HomeFragment.TAG, "got $response")
try {
val data = JSONObject(response.toString())
val items = data.getJSONObject("data")
Log.e(HomeFragment.TAG, "username=" + items.getString("url"))
info.username = items.getString("url")
Log.e(HomeFragment.TAG, "username second=" + info.username)
info.bio = items.getString("bio")
info.avatar = items.getString("avatar")
info.cover = items.getString("cover")
info.created = items.getLong("created")
info.reputation = items.getDouble("reputation")
} catch (e: JSONException) {
Log.e(HomeFragment.TAG, "Parsing error: " + e.message)
}
}, Response.ErrorListener { error ->
Log.e(HomeFragment.TAG, "Response error: " + error.message)
}) {
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers["Authorization"] = "Bearer ${accessToken}"
headers["User-Agent"] = "epicture"
return headers
}
}
// Adding request to request queue
AppController.instance?.addToRequestQueue(jsonObjectRequest)
Log.e(HomeFragment.TAG, "username last=" + info.username)
return info
}
The problem is that the function always returns default initialized object (with "username" for username for exemple) and not the string parsed in my JsonObjectRequest, even if the parsing is working (logs are correct)...
If you have some help or ideas thank you!
You should wait until JsonObjectRequest has finished and returned the result from server then you got the result , You get the result inside Response.Listener callback but the info object that you returned doesn't updated.
use async wait or a suspend function to fetch info from server then return it
Update :
Here is the code :
fun getAccountInfo(
accessToken: String,
onSuccess: (account: AccountInfo) -> Unit,
onError: (e: Exception) -> Unit
) {
val jsonObjectRequest: JsonObjectRequest = object : JsonObjectRequest(
Method.GET,
"https://api.imgur.com/3/account/me",
null,
Response.Listener { response ->
try {
val data = JSONObject(response.toString())
val items = data.getJSONObject("data")
val info = AccountInfo(
username = items.getString("url"),
bio = items.getString("bio"),
avatar = items.getString("avatar"),
cover = items.getString("cover"),
created = items.getLong("created"),
reputation = items.getDouble("reputation")
)
onSuccess.invoke(info) // request success so call this
} catch (e: JSONException) {
onError.invoke(e)
}
},
Response.ErrorListener { error ->
onError.invoke(error)
}) {
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers["Authorization"] = "Bearer $accessToken"
headers["User-Agent"] = "epicture"
return headers
}
}
AppController.instance?.addToRequestQueue(jsonObjectRequest)
}
Call this on your Activity
getAccountInfo("access token",
onSuccess = {
// todo show info in UI
Log.d(TAG, "onCreate: onSuccess : $it")
},
onError = {
// toast error or show custom error view
Log.e(TAG, "onCreate: onError: ", it)
}
)
I'm facing with error for below code
may someone help me
I'm trying to register by this code to an app
registration with VOLLEY and KOTLIN issue , can somebody say me what is issue I'm getting followed error :
I am adding this for more details to can post my question
W/System.err: org.json.JSONException: Value <br of type java.lang.String cannot be converted to JSONObject
at org.json.JSON.typeMismatch(JSON.java:111)
`override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loading = findViewById<ProgressBar>(R.id.loading)
name = findViewById<EditText>(R.id.name)
email = findViewById<EditText>(R.id.email)
password = findViewById<EditText>(R.id.password)
c_password = findViewById<EditText>(R.id.password)
btn_regist = findViewById<Button>(R.id.btn_regist)
btn_regist.setOnClickListener() { v -> regist() }
}
private fun regist(){
loading.visibility = View.VISIBLE
btn_regist.visibility = View.GONE
val name:String = this.name.text.trim().toString()
val email:String = this.email.text.trim().toString()
val password:String = this.password.text.trim().toString()
val stringRequest = object : StringRequest(com.android.volley.Request.Method.POST, URL_REGIST,
com.android.volley.Response.Listener<String>() { response ->
try {
val jsonObject = JSONObject(response)
val success : String = jsonObject.getString("success")
if (success.equals("1")){
Toast.makeText(this, "Register Success ! Eyyvalll !", Toast.LENGTH_SHORT).show()
}
}
catch (e: JSONException){
e.printStackTrace()
Toast.makeText(this, "Register Error ! goh !!!! :/" + e.toString() , Toast.LENGTH_SHORT).show()
loading.visibility = View.GONE
btn_regist.visibility = View.VISIBLE
}
}, com.android.volley.Response.ErrorListener {
error -> error.printStackTrace()
Toast.makeText(this, "Register Error ! SHIT :/" + error.toString() , Toast.LENGTH_SHORT).show()
loading.visibility = View.GONE
btn_regist.visibility = View.VISIBLE
}) {
// #Throws(AuthFailureError::class)
override fun getParams(): Map<String, String> {
val params = HashMap<String, String>()
params["name"] = name
params["email"] = email
params["password"] = password
return params
}
}
val requestQueue = Volley.newRequestQueue(this)
requestQueue.add(stringRequest)
}`
I thing this error because response result is not a valid json format
com.android.volley.Response.Listener<String>() { response ->
try {
//at this point,you can debug result with console log your response
//because if response have a php error then json format not valid
Log.d("RESPONSE"," $response")
val jsonObject = JSONObject(response)
//at this you can check if json has have key name success
if (jsonObject.has("success")){
val success : String = jsonObject.getString("success")
if (success.equals("1")){
Toast.makeText(this, "Register Success ! Eyyvalll !", Toast.LENGTH_SHORT).show()
}
}
}
......
hope this help