Android Question [SOLVED]Casting issues within a For Each iterating data from an API

Daniel44

Active Member
Licensed User
Hey!

I'm working with my API made in PHP . Afer I got logged it returns an Access Token and a Refresh Token in json format. Like this:

Postman:

B4X:
{
    "statusCode": 201,
    "success": true,
    "messages": [],
    "data": {
        "session_id": 9,
        "access_token": "ZDk0M2E2OGMwZjQ2OGVhNWE3YTUwY2E1OWJjODZkNmQ4NzhmMjAwMzFmNDEzOGIyMTY0MDMwNjA3NA==",
        "access_token_expires_in": 1200,
        "refresh_token": "MzQ5ODg0ZGFhYjNjNjUwNWMwOTFhZTY0OGNkODJlZTUxNWQ1Mzc3NmExYjc0YWY5MTY0MDMwNjA3NA==",
        "refresh_token_expires_in": 1209600
    }
}

With B4A I'm doing this to return to my app that data

B4X:
Dim ok As HttpJob
    ok.Initialize("",Me)
    ok.PostString(endpointUrl,data)
    ok.GetRequest.SetContentType("application/json")
  
    Wait For (ok) Jobdone(ok As HttpJob)
    If ok.Success Then
        Log(ok.GetString)
      
        Log("DATA SENT TO SERVER  RESPONSE SUCCESS")

B4X:

This returns:

B4X:
{"statusCode":201,"success":true,"messages":[],"data":{"session_id":65,"access_token":"NTE3NzUzZmU3Yjk4ZDdjMmQ1YmQ0MzZjYzE0NThmMzBiNDhhZTBjOGJiMmUwYmU3MTY0MDM4NTAwOA==","access_token_expires_in":1200,"refresh_token":"YmY5YWYyYWI4OTQyOGMyOWI5YzE5YjBjYmY2NTAzNGY0YTc1YWM2NTMwN2Y0YzVlMTY0MDM4NTAwOA==","refresh_token_expires_in":1209600}}

'till here everything works fine... But I need to work and handle that response (work with access token etc..)

To do that I'm trying to do this..

B4X:
Dim parser As JSONParser
        parser.Initialize(ok.GetString)
        Dim mRoot As Map = parser.NextObject
        Dim datos As List = mRoot.Get("data")
        For Each resl As Map In datos
          
            Dim SessionID As Int = resl.Get("session_id")
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")
          
            Dim row(4) As Object
            row(0) = SessionID
            row(1) = AT
            row(2) = RT
            row(3) = T_EXPIRES
          
            'datasessionlist.Add(row)
          
            Log(datos)
            Log("-----------")
            Log(AT)
            Log("-----------")
            Log(RT)
            Log("-----------")
            Log(T_EXPIRES)
            Log("---FIN DE LOS DATOS----")
          
        Next

But.. it doesn't work.. I got this error log...

B4X:
Error occurred on line: 141 (Main)
java.lang.ClassCastException: anywheresoftware.b4a.objects.collections.Map$MyMap cannot be cast to java.util.List
    at anywheresoftware.b4a.objects.collections.List.getSize(List.java:129)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:348)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.shell.DebugResumableSub$RemoteResumableSub.resume(DebugResumableSub.java:22)
    at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:137)
    at anywheresoftware.b4a.BA$2.run(BA.java:387)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

and I don't know what I'm doing wrong! Thank you in advance!!!
 
Last edited:
Solution
for simplicity's sake, just call:
B4X:
            Dim SessionID As Int = resl.Get("session_id") '<----------line 143
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")

you don't need "for each ... as". a map (datos) is not iterable. it is possible to iterate through a map's keys, but not through the map itself. you seem fascinated by "foreach ... as", so if you have to do it, it would be something like:
B4X:
for each k as string in datos.keys
   log(k & " " & datos.get(k))
next

this is ok for stepping through a map where the keys are not known, and you want to find out what they are, but, in...

drgottjr

Well-Known Member
Licensed User
Longtime User
datos is a map, not a list.
your code should be:
B4X:
        Dim datos As Map = mRoot.Get("data")

just because it has a number of "fields" doesn't make it a list. just use your normal map.get() methods.
datos.get("access_token"), datos.get("session_id"), datos.get("access_token_expires_in"), etc.
 
Upvote 0

Daniel44

Active Member
Licensed User
datos is a map, not a list.
your code should be:
B4X:
        Dim datos As Map = mRoot.Get("data")

just because it has a number of "fields" doesn't make it a list. just use your normal map.get() methods.
datos.get("access_token"), datos.get("session_id"), datos.get("access_token_expires_in"), etc.
Hey drgottjr Thank you for answering.. well I've done that but it shows me:

B4X:
Error occurred on line: 143 (Main)
java.lang.RuntimeException: Object should first be initialized (Map).
    at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:67)
    at anywheresoftware.b4a.objects.collections.Map.Get(Map.java:64)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:348)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.shell.DebugResumableSub$RemoteResumableSub.resume(DebugResumableSub.java:22)
    at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:137)
    at anywheresoftware.b4a.BA$2.run(BA.java:387)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
** Activity (main) Pause, UserClosed = true **


B4X:
For Each resl As Map In datos
            
            Dim SessionID As Int = resl.Get("session_id") '<----------line 143
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")
 
Upvote 0

drgottjr

Well-Known Member
Licensed User
Longtime User
for simplicity's sake, just call:
B4X:
            Dim SessionID As Int = resl.Get("session_id") '<----------line 143
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")

you don't need "for each ... as". a map (datos) is not iterable. it is possible to iterate through a map's keys, but not through the map itself. you seem fascinated by "foreach ... as", so if you have to do it, it would be something like:
B4X:
for each k as string in datos.keys
   log(k & " " & datos.get(k))
next

this is ok for stepping through a map where the keys are not known, and you want to find out what they are, but, in your case, you already know what you're looking for. just "get()" the value you want and move on with your life.
 
Upvote 1
Solution

Omar Parra A.

Expert
Licensed User
Longtime User
Use ".AS"
To validate if it exists, use "GetDefault" or "ContainsKey"
Sample:
B4X:
    Dim sText As String = $"{"statusCode":201,"success":true,"messages":["Hello","I am","Message"],"data":{"session_id":65,"access_token":"NTE3NzUzZmU3Yjk4ZDdjMmQ1YmQ0MzZjYzE0NThmMzBiNDhhZTBjOGJiMmUwYmU3MTY0MDM4NTAwOA==","access_token_expires_in":1200,"refresh_token":"YmY5YWYyYWI4OTQyOGMyOWI5YzE5YjBjYmY2NTAzNGY0YTc1YWM2NTMwN2Y0YzVlMTY0MDM4NTAwOA==","refresh_token_expires_in":1209600}}"$
    Dim mRoot As Map = sText.As(JSON).ToMap

    Log("----- STATUS ------")

    Dim Success As String = mRoot.Get("success")
    Dim StatusCode As Int = mRoot.Get("statusCode")
    Dim Messages As List = mRoot.GetDefault("messages","[]")

    Log($"Success : ${Success}"$)
    Log($"StatusCode : ${StatusCode}"$ )
    Log("Messages:")
    For Each msg As Object In Messages
        Log(msg)
    Next
    
    Log("----- DATA ------")
    If mRoot.ContainsKey("data") Then
        Dim Data As Map = mRoot.Get("data")
        Dim SessionID As Int = Data.GetDefault("session_id", 0)
        Dim Access_Token As String = Data.GetDefault("access_token","")
        Dim Access_Token_Expires_In As Int = Data.GetDefault("access_token_expires_in", 0)
        Dim Refresh_Token As String = Data.GetDefault("refresh_token","")
        Dim Refresh_Token_Expires_In As Int = Data.GetDefault("refresh_token_expires_in",0)

        Log($"SessionID : ${SessionID}"$)
        Log($"Access_Token : ${Access_Token}"$)
        Log($"Access_Token_Expires_In : ${Access_Token_Expires_In}"$)
        Log($"Refresh_Token : ${Refresh_Token}"$)
        Log($"Refresh_Token_Expires_In : ${Refresh_Token_Expires_In}"$)
    End If

    Log("--- END ----")
1640401475564.png
 
Last edited:
Upvote 1

Daniel44

Active Member
Licensed User
for simplicity's sake, just call:
B4X:
            Dim SessionID As Int = resl.Get("session_id") '<----------line 143
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")

you don't need "for each ... as". a map (datos) is not iterable. it is possible to iterate through a map's keys, but not through the map itself. you seem fascinated by "foreach ... as", so if you have to do it, it would be something like:
B4X:
for each k as string in datos.keys
   log(k & " " & datos.get(k))
next

this is ok for stepping through a map where the keys are not known, and you want to find out what they are, but, in your case, you already know what you're looking for. just "get()" the value you want and move on with your life.
Hey Oparra thank you it is working!
 
Upvote 0

Daniel44

Active Member
Licensed User
Use ".AS"
To validate if it exists, use "GetDefault" or "ContainsKey"
Sample:
B4X:
    Dim sText As String = $"{"statusCode":201,"success":true,"messages":["Hello","I am","Message"],"data":{"session_id":65,"access_token":"NTE3NzUzZmU3Yjk4ZDdjMmQ1YmQ0MzZjYzE0NThmMzBiNDhhZTBjOGJiMmUwYmU3MTY0MDM4NTAwOA==","access_token_expires_in":1200,"refresh_token":"YmY5YWYyYWI4OTQyOGMyOWI5YzE5YjBjYmY2NTAzNGY0YTc1YWM2NTMwN2Y0YzVlMTY0MDM4NTAwOA==","refresh_token_expires_in":1209600}}"$
    Dim mRoot As Map = sText.As(JSON).ToMap

    Log("----- STATUS ------")

    Dim Success As String = mRoot.Get("success")
    Dim StatusCode As Int = mRoot.Get("statusCode")
    Dim Messages As List = mRoot.GetDefault("messages","[]")

    Log($"Success : ${Success}"$)
    Log($"StatusCode : ${StatusCode}"$ )
    Log("Messages:")
    For Each msg As Object In Messages
        Log(msg)
    Next
   
    Log("----- DATA ------")
    If mRoot.ContainsKey("data") Then
        Dim Data As Map = mRoot.Get("data")
        Dim SessionID As Int = Data.GetDefault("session_id", 0)
        Dim Access_Token As String = Data.GetDefault("access_token","")
        Dim Access_Token_Expires_In As Int = Data.GetDefault("access_token_expires_in", 0)
        Dim Refresh_Token As String = Data.GetDefault("refresh_token","")
        Dim Refresh_Token_Expires_In As Int = Data.GetDefault("refresh_token_expires_in",0)

        Log($"SessionID : ${SessionID}"$)
        Log($"Access_Token : ${Access_Token}"$)
        Log($"Access_Token_Expires_In : ${Access_Token_Expires_In}"$)
        Log($"Refresh_Token : ${Refresh_Token}"$)
        Log($"Refresh_Token_Expires_In : ${Refresh_Token_Expires_In}"$)
    End If

    Log("--- END ----")
View attachment 123409
for simplicity's sake, just call:
B4X:
            Dim SessionID As Int = resl.Get("session_id") '<----------line 143
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")

you don't need "for each ... as". a map (datos) is not iterable. it is possible to iterate through a map's keys, but not through the map itself. you seem fascinated by "foreach ... as", so if you have to do it, it would be something like:
B4X:
for each k as string in datos.keys
   log(k & " " & datos.get(k))
next

this is ok for stepping through a map where the keys are not known, and you want to find out what they are, but, in your case, you already know what you're looking for. just "get()" the value you want and move on with your life.
for simplicity's sake, just call:
B4X:
            Dim SessionID As Int = resl.Get("session_id") '<----------line 143
            Dim AT As String = resl.Get("access_token")
            Dim RT As String = resl.Get("refresh_token")
            Dim T_EXPIRES As Float = resl.Get("access_token_expires_in")

you don't need "for each ... as". a map (datos) is not iterable. it is possible to iterate through a map's keys, but not through the map itself. you seem fascinated by "foreach ... as", so if you have to do it, it would be something like:
B4X:
for each k as string in datos.keys
   log(k & " " & datos.get(k))
next

this is ok for stepping through a map where the keys are not known, and you want to find out what they are, but, in your case, you already know what you're looking for. just "get()" the value you want and move on with your life.
Hey thank you !! now it is working ! Well I'm not fascinated by for each hahaha just thought I was on the right way. Thank you!
 
Upvote 0
Top