Android Question List of objects not working as expected

RogerW

Member
this is my first attempt at writing an api client in B4X, I have worked out many of the issues but this list issue has me baffled, any assistance would be greatly appreciated.

I am processing a JSON response into a class, I have created a class for a single item and then another class for a list of items. When I process the JSON response into the class I get a list of the correct number of items but they are all the last item.

I have gone through the code one loop at a time and it seems that it adds the first item to the list, and then when it adds the second item it overwrites the first item as well, it does this in each loop until we have the correct number of items in the list but they are all duplicates of the last item.

This is the offending code:
' Class Appointments Module
Sub Class_Globals
    Public appointmentList As List
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
    appointmentList.Initialize
End Sub

public Sub parseJSON(jsonString As String)
    Dim myAppointment As Appointment   
    Dim JSON As JSONParser
    Dim map1 As Map
    Dim m As Map
    Dim apptList As List
    Dim myList As List

    
    myList.Initialize
            
    JSON.Initialize(jsonString)
    map1 = JSON.NextObject
    apptList = map1.Get("appointments")

    Log("apptList.Size = " & apptList.Size)

    For i = 0 To apptList.Size - 1
        myAppointment.Initialize
        m = apptList.Get(i)
        myAppointment.serv_id = (m.Get("serv_id"))
        myAppointment.cust_id = (m.Get("cust_id"))
        myAppointment.support_id = (m.Get("support_id"))
        myAppointment.invoice_id = (m.Get("invoice_id"))
        myAppointment.serv_desc = (m.Get("serv_desc"))
        myAppointment.status_id = (m.Get("status_id"))
        myAppointment.date_entered = (m.Get("date_entered"))
        myAppointment.date_expected = (m.Get("date_expected"))
        myAppointment.date_due = (m.Get("date_due"))
        myAppointment.tech_id = (m.Get("tech_id"))
        myAppointment.location_id = (m.Get("location_id"))
        myAppointment.start_tests = (m.Get("start_tests"))
        myAppointment.complete_tests = (m.Get("complete_tests"))
        myAppointment.on_bench = (m.Get("on_bench"))
        myAppointment.completed = (m.Get("completed"))
        myAppointment.start_waiting = (m.Get("start_waiting"))
        myAppointment.minutes_waiting = (m.Get("minutes_waiting"))
        myAppointment.status_changed = (m.Get("status_changed"))
        myAppointment.priority = (m.Get("priority"))
        appointmentList.Add(myAppointment)
        Log("Adding " & myAppointment.serv_id & " as Appointment " & i)
    Next
    
End Sub

The second class is very simple just to store the variables:
Sub Class_Globals
    Public serv_id As Int
    Public cust_id As Int
    Public support_id As Int
    Public invoice_id As Int
    Public serv_desc As String
    Public status_id As Int
    Public date_entered As String
    Public date_expected As String
    Public date_due As String
    Public tech_id As Int
    Public location_id As Int
    Public start_tests As String
    Public complete_tests As String
    Public on_bench As String
    Public completed As String
    Public start_waiting As String
    Public minutes_waiting As Int
    Public status_changed As Int
    Public priority As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
    
End Sub
The main block:
#Region  Project Attributes
    #ApplicationLabel: Nortech API Test
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False   
#End Region

#Region  Activity Attributes
    #FullScreen: True
    #IncludeTitle: False
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    
    

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

    Private Label1 As Label
    Private Button1 As Button
    Private ListView1 As ListView
    Private Label2 As Label
    Private apptDetail As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("API_Test")

End Sub

Sub Activity_Resume
    refresh   
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub


private Sub refresh()
    ListView1.Visible = True
    apptDetail.Visible = False
    ListView1.Clear
    Label1.Text = "Fetching data"
    Dim Job As HttpJob
    Job.Initialize("api", Me)   ' Init HttpJob
    Job.PostString("https://nortechcomputers.co.uk/nortech/v1.0/api/index.php?secret=123456","") ' POST to url
End Sub

Sub JobDone(Job As HttpJob)
    If Job.Success Then
        Dim res As String
        Dim myAppointments As Appointments
        Dim myAppointment As Appointment
        res = Job.GetString                  ' Result from php store to result
        Log("Response from server: " & res)  ' Show all of result from server to log
        
        myAppointment.Initialize
        myAppointments.Initialize
        myAppointments.parseJSON(res)
        

        For i = 0 To myAppointments.appointmentList.Size - 1
            'ListView1.AddSingleLine(myAppointments.appointmentList.get(i))
            myAppointment = myAppointments.appointmentList.Get(i)           
            Log ("Reading myAppointment.serv_id = " & myAppointment.serv_id & " as appointment " & i)           
        Next
        
    Else
        Log(Job.ErrorMessage)                    ' Error detect
        ToastMessageShow("Error: " & Job.ErrorMessage, True)
    End If
    Job.Release
End Sub

Sub Button1_Click
    refresh
End Sub

Sub ListView1_ItemClick (Position As Int, Value As Object)
    apptDetail.Visible = True
    ListView1.Visible = False
    apptDetail.Text = "Appointment   Position = " & Position & " Value = " & Value
End Sub


I have no idea where I have gone wrong but clearly this is not doing what i expect, hopefully someone can spot what I can't see.

Thanks,
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. Move this line inside the For loop:
B4X:
Dim myAppointment As Appointment

2. Consider making Appointment a custom Type instead of class. This will allow you to serialize it very easily with B4XSerializator.
3. You should never have a JobDone sub. Learn how to use Wait For. Much simpler.
4. If it is a new project then it will be easier to use B4XPages.
 
Upvote 0

RogerW

Member
Thank you for your prompt reply.

Yes moving the Dim statement as shown above made the difference, it feels a little strange having the Dim statement execute on each iteration of the loop but I guess this is to create a new instance each time, as clearly I was altering the same instance each time.

This is a small learning project for me, so I grabbed the code for retrieving the JSON response from an example online, I will take a look at Wait for and see if I can make that change.

Investigating the B4XSerializator is on my to do list, as is B4Pages now.

Thanks again.
 
Upvote 0

RogerW

Member
I have removed the JobDone Sub and replaced it with a new sub that waits for a reply from the server.

SUB getJsonFromServer:
Private Sub getJsonFromServer()
    Dim Job As HttpJob
    Dim res As String
    Dim myAppointments As Appointments
    
    Job.Initialize("api", Me)   ' Init HttpJob
    Job.PostString("https://nortechcomputers.co.uk/nortech/v1.0/api/index.php?secret=123456","") ' POST to url
    Wait For (Job) JobDone(Job As HttpJob)
    If Job.Success Then
        res = Job.GetString
        Log("Response from server: " & res)  ' Show all of result from server to log
        myAppointments.Initialize
        myAppointments.parseJSON(res)
        displayAppointments(myAppointments)
    End If
    Job.Release
End Sub
Amended Sub Refresh():
private Sub refresh()
    ListView1.Visible = True
    apptDetail.Visible = False
    ListView1.Clear
    Label1.Text = "Fetching data"
    getJsonFromServer
End Sub
 
Upvote 0
Top