JSON Parser – How to Use

canalrun

Well-Known Member
Licensed User
Longtime User
Hello,
I have a web service written in PHP that returns a JSON string to a B4A app. The JSON string is generated by PHP 5. The string is basically a PHP $_Session variable and contains two objects that are arrays of arrays and several scalar values. I show the string as the first line in the Log output below.

I have listed the code I'm using also. What I would like to be able to do is parse the returned JSON into Maps containing the values, arrays, and objects. I would like the code to be somewhat general-purpose so I don't need to modify it each time the returned string changes a little.

CODE:
B4X:
          Dim js As JSONParser
          Dim m As Map
        Dim l As List
        Dim b As Boolean
        Dim i As Int

          s = HttpUtils.GetString(PostUrl)
          Log(s)
        
        m.Initialize
        l.Initialize
        
        js.Initialize(s)
          Log("1:")
        
          Try
            l = js.NextArray
            Log(l)
          Catch
            Log(LastException.Message)
          End Try
        
        js.Initialize(s)
          Log("2:")
        
          Try
            t = js.NextValue
            Log(t)
          Catch
            Log(LastException.Message)
          End Try
        
        js.Initialize(s)
          Log("0:")
        
          Try
            m = js.NextObject
            Log(m)
          Catch
            Log(LastException.Message)
          End Try

     Exit


OUTPUT (from Android Log):
B4X:
{"tst1":"test1","groups1":[{"pos":1,"gid":"1","gname":"Family","lastaccess":"100"},{"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"},{"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}],"tst2":"test2","groups2":[{"pos":1,"gid":"1","gname":"Family","lastaccess":"100"},{"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"},{"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}]}
1:
java.lang.RuntimeException: JSON Array expected.
2:
java.lang.RuntimeException: Simple value expected.
0:
(MyMap) {groups1=[{gname=Family, gid=1, lastaccess=100, pos=1}, {gname=Friends, gid=2, lastaccess=120, pos=2}, {gname=Coworkers, gid=3, lastaccess=130, pos=3}], groups2=[{gname=Family, gid=1, lastaccess=100, pos=1}, {gname=Friends, gid=2, lastaccess=120, pos=2}, {gname=Coworkers, gid=3, lastaccess=130, pos=3}], tst1=test1, tst2=test2}

Is the source code for the JSON Parser available?

Thanks,
Barry.
 

canalrun

Well-Known Member
Licensed User
Longtime User
Hello,
I couldn't seem to get the JSON parser to do what I needed, so I wrote my own. I've pasted it below, I hope somebody finds it useful.

In addition to my parser, which I call YAJP (not very creative), I also included a snippet of my test code and results from the Log.

I took a brute force approach – I don't expect to win many beauty contests. I have the parser in a separate B4A Code Module.

The first line of the "Log Output" shows the JS0N string I need to parse. It contains two scalar values and two arrays of arrays.

YAJP returns a map separating the top level of key/value pairs represented in the JSON string. In order to parse the arrays of arrays, YAJP needs to be invoked multiple times.

Log Output:
B4X:
JSON string:
{"tst1":"test1","groups1":[{"pos":1,"gid":"1","gname":"Family","lastaccess":"100"},{"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"},{"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}],"tst2":"test2","groups2":[{"pos":1,"gid":"1","gname":"Family","lastaccess":"100"},{"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"},{"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}]}
m1:
0: tst1=test1
1: groups1=[{"pos":1,"gid":"1","gname":"Family","lastaccess":"100"},{"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"},{"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}]
2: tst2=test2
3: groups2=[{"pos":1,"gid":"1","gname":"Family","lastaccess":"100"},{"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"},{"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}]
m2:
0: 0={"pos":1,"gid":"1","gname":"Family","lastaccess":"100"}
1: 1={"pos":2,"gid":"2","gname":"Friends","lastaccess":"120"}
2: 2={"pos":3,"gid":"3","gname":"Coworkers","lastaccess":"130"}
m3:
0: pos=1
1: gid=1
2: gname=Family
3: lastaccess=100

Test Code:
B4X:
If HttpUtils.IsSuccess(PostUrl) Then
  Dim m1, m2, m3 As Map

  s = HttpUtils.GetString(PostUrl)
        
  m1.Initialize
  m2.Initialize
  m2.Initialize
        
  Log("JSON string:")
  Log(s)

  Log("m1:")
  m1 = YAJP.yajp(s)
  For i=0 To m1.Size-1
    Log(i & ": " & m1.GetKeyAt(i) & "=" & m1.GetValueAt(i))
  Next

  Log("m2:")
  m2 = YAJP.yajp(m1.Get("groups1"))
  For i=0 To m2.Size-1
    Log(i & ": " & m2.GetKeyAt(i) & "=" & m2.GetValueAt(i))
  Next

  Log("m3:")
  m3 = YAJP.yajp(m2.GetValueAt(0))
  For i=0 To m3.Size-1
    Log(i & ": " & m3.GetKeyAt(i) & "=" & m3.GetValueAt(i))
  Next

  Exit

YAJP B4A module
B4X:
'Code module
'Subs in this code module will be accessible from all modules.
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 yajp(jstr As String) As Map
  Dim i, j, pos, lvl As Int
  Dim s, t As String
  Dim oc, cc As String ' opening and closing characters
  Dim obc(2), arc(2), sepc As String ' object char, array char, separator char
  Dim jm As Map
  Dim l1 As List
  Dim ex As ExceptionEx
  
  jm.Initialize
  l1.Initialize
  lvl = 0
  pos = 0
  obc(0) = "{"
  obc(1) = "}"
  arc(0) = "["
  arc(1) = "]"
  sepc = ","
  
  oc = jstr.CharAt(pos)
  pos = pos + 1
  
  If (oc = obc(0)) Then
    lvl = lvl + 1
    cc = "}"
  Else If (oc = arc(0)) Then
    lvl = lvl + 1
    cc = "]"
  Else
    ex.Initialize("jstr first char not object or array begin character.")
   ex.Throw
  End If
  
  ' divide substrings at sepc's until cc reached at same level
  t = ""
  
  Do While (pos < jstr.Length)
    s = jstr.CharAt(pos)
   pos = pos + 1
   
   If ((s = cc) AND (lvl = 1)) Then ' all done
     If (t.Length > 0) Then l1.Add(t)
     Exit
   End If
   
   If (pos >= jstr.Length) Then
     ex.Initialize("No JSON terminating char: " & cc)
     ex.Throw
   End If
   
   If ((s = obc(0)) OR (s = arc(0))) Then
     lvl = lvl + 1
   End If
   
   If ((s = obc(1)) OR (s = arc(1))) Then
     lvl = lvl - 1
   End If
   
   If ((lvl = 1) AND (s = sepc)) Then
     If (t.Length > 0) Then l1.Add(t)
     t = ""
   Else
     t = t & s
   End If
  Loop
  
'  Log("l1:")
'  For i=0 To l1.Size-1
'    Log(i & ": " & l1.Get(i))
'  Next
    
  ' divide list "elements" into key=value in map
  Dim kv(2) As String
  
  If (oc = arc(0)) Then ' array elements
    For i=0 To l1.Size-1
      kv(0) = i
      kv(1) = l1.Get(i)

      jm.Put(kv(0), kv(1))
    Next
  
  Else ' object elements
    sepc = ":"
  
    For i=0 To l1.Size-1
      s = l1.Get(i)
      j = s.IndexOf(sepc)
   
      If ((j > 0) AND (j < s.Length-1)) Then
        kv(0) = s.SubString2(0, j)
        kv(1) = s.SubString(j + 1)

        j = kv(0).Length
     
        If ((kv(0).CharAt(0) = QUOTE) AND (kv(0).CharAt(j-1) = QUOTE)) Then
          s = kv(0).SubString2(1, j-1)
          kv(0) = s
        End If
     
        j = kv(1).Length
     
        If ((kv(1).CharAt(0) = QUOTE) AND (kv(1).CharAt(j-1) = QUOTE)) Then
          s = kv(1).SubString2(1, j-1)
          kv(1) = s
        End If
     
        jm.Put(kv(0), kv(1))
      Else
        ex.Initialize("key value error at " & j & ", " & s)
        ex.Throw
      End If
    Next
  End If
  
'  Log("jm:")
'  For i=0 To jm.Size-1
'    Log(i & ": " & jm.GetKeyAt(i) & " : " & jm.GetValueAt(i))
'  Next
  
  Return(jm)
End Sub

Barry.
 
Last edited:
Upvote 0
Top