B4J Question There is a short way to access directly a value/list inside a JSON inside several nested lists?

Giu

Member
Licensed User
Longtime User
Hi all,

I got a JSON object from a request as Map using JSONParser

B4X:
        Dim response As Map = jp.NextObject
        Dim firstList As Map = response.Get("firstList")
        Dim secondList As Map = firstList.Get("ListInsideFirstList")
        Dim thirdList As Map = secondList.Get("ListInsideSecondList")

Imagine something like the code above. This works, now I can iterate over thirdList and take the values I want, but, I haev to check each item if exists to make sure I can go and have values in the last step. There is a short way, to go directly to thirdList and check if exists?

Something like

B4X:
        Dim response As Map = jp.NextObject
        Dim theListIwant As Map = response.Get("firstList").As(Map).Get("ListInsideFirstList").As(Map).Get("ListInsideSecondList")
if (theListIwant.IsInitialized) <---- then the tree can be done and there is a path to ListInsideSecondList with values

I tried the code above but my program just crashes, It stops on a breakpoint I put after "Dim" line, and program stops without any error.
 
Solution
See:
B4X:
    Dim sText As String =  $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$
               
    Dim theListIwant As Map = sText.As(JSON).ToMap.Get("firstList").As(Map).Get("ListInsideFirstList").As(Map).Get("ListInsideSecondList")
    If Not(theListIwant.Size = 0) Then
        Log(theListIwant.Get("Data").As(Map).Get("field1"))...

TILogistic

Expert
Licensed User
Longtime User
See:
B4X:
    Dim sText As String =  $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$
               
    Dim theListIwant As Map = sText.As(JSON).ToMap.Get("firstList").As(Map).Get("ListInsideFirstList").As(Map).Get("ListInsideSecondList")
    If Not(theListIwant.Size = 0) Then
        Log(theListIwant.Get("Data").As(Map).Get("field1"))
        Log(theListIwant.Get("Data").As(Map).Get("field2"))
    End If

or
Check exist:
If theListIwant.ContainsKey("Data") Then
1676588828564.png
 
Upvote 0
Solution

aaronk

Well-Known Member
Licensed User
Longtime User
Try:
B4X:
Dim sText As String =  $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$

    Dim parser As JSONParser
    parser.Initialize(sText)
    Dim jRoot As Map = parser.NextObject
    Dim firstList As Map = jRoot.Get("firstList")
    Dim ListInsideFirstList As Map = firstList.Get("ListInsideFirstList")
    Dim ListInsideSecondList As Map = ListInsideFirstList.Get("ListInsideSecondList")
   
    If ListInsideSecondList.ContainsKey("Data") Then
        Dim Data As Map = ListInsideSecondList.Get("Data")
   
        For Each key As String In Data.Keys
            Log($"${key} = ${Data.Get(key)}"$)
        Next
    End If

field1 = 1
field2 = 2
 
Upvote 0

TILogistic

Expert
Licensed User
Longtime User
direct:
B4X:
    Dim theListIwant As Map = sText.As(JSON).ToMap.Get("firstList").As(Map).Get("ListInsideFirstList").As(Map).Get("ListInsideSecondList")
    If theListIwant.ContainsKey("Data") Then
        For Each key As String In theListIwant.Get("Data").As(Map).Keys
            Log($"${key} = ${theListIwant.Get("Data").As(Map).Get(key)}"$)
        Next
    End If
1676609403337.png
 
Upvote 0

Giu

Member
Licensed User
Longtime User
See:
B4X:
    Dim sText As String =  $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$
              
    Dim theListIwant As Map = sText.As(JSON).ToMap.Get("firstList").As(Map).Get("ListInsideFirstList").As(Map).Get("ListInsideSecondList")
    If Not(theListIwant.Size = 0) Then
        Log(theListIwant.Get("Data").As(Map).Get("field1"))
        Log(theListIwant.Get("Data").As(Map).Get("field2"))
    End If

or
Check exist:

View attachment 139403
Checking your code looks similar to what I suggested to do and diving, I noticed the crash comes trying to get the end of the tree. I will ask on another thread because my question now is more related to the error I get, more than the way to do it.

Thanks to both.
 
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
I use this class to quickly access something in JSON:

class JSONQuery
B4X:
Sub Class_Globals
    Private jsonM As Map
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(jsonString As String)
    Dim jsonP As JSONParser
    jsonP.Initialize(jsonString)
    jsonM = jsonP.NextObject
End Sub

public Sub Query(path As String, default As Object) As Object
    Dim ret As Object = InnerQuery(jsonM, path)
    If ret = Null Then
        ret = default
    End If
    Return ret
End Sub

private Sub InnerQuery(obj As Object, path As String) As Object
    Dim keys() = Regex.Split("/", path) As String : path = ""
    If keys.Length = 0 Then Return obj
    Dim key As String
    For i = 0 To (keys.Length - 1)
        key = keys(i) : If key <> "" Then Exit
    Next
    If key.Length = 0 Then Return obj
    For j = i + 1 To (keys.Length - 1)
        path = path & keys(j) & "/"
    Next
    If obj Is Map Then
        Dim map = obj As Map
        obj = InnerQuery(map.GetDefault(key, Null), path)
    Else If obj Is List Then
        Dim lst = obj As List
        Try
            obj = InnerQuery(lst.Get(key), path)
        Catch
            obj = Null
        End Try
    End If
    Return obj
End Sub

Usage:
B4X:
Dim jsonString As String = $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$
 
 
Dim jsonQ As JSONQuery
jsonQ.Initialize(jsonString)
 
' second param is a default if not found
Log(jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data/field1", "0")) ' returns 1
Log(jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data/field2", "0")) ' returns 2
Log(jsonQ.Query("firstList/doesNotExist/field3", "0")) ' returns 0, the default param

Dim m As Map = jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data", Null)
If m <> Null Then
    For Each key As String In m.Keys
        Log(key & ": " & m.Get(key))
    Next
End If

Alwaysbusy
 
Last edited:
Upvote 0

Giu

Member
Licensed User
Longtime User
I use this class to quickly access something in JSON:

class JSONQuery
B4X:
Sub Class_Globals
    Private jsonM As Map
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(jsonString As String)
    Dim jsonP As JSONParser
    jsonP.Initialize(jsonString)
    jsonM = jsonP.NextObject
End Sub

public Sub Query(path As String, default As Object) As Object
    Dim ret As Object = InnerQuery(jsonM, path)
    If ret = Null Then
        ret = default
    End If
    Return ret
End Sub

private Sub InnerQuery(obj As Object, path As String) As Object
    Dim keys() = Regex.Split("/", path) As String : path = ""
    If keys.Length == 0 Then Return obj
    Dim key As String
    For i = 0 To (keys.Length - 1)
        key = keys(i) : If key <> "" Then Exit
    Next
    If key.Length == 0 Then Return obj
    For j = i + 1 To (keys.Length - 1)
        path = path & keys(j) & "/"
    Next
    If obj Is Map Then
        Dim map = obj As Map
        obj = InnerQuery(map.GetDefault(key, Null), path)
    Else If obj Is List Then
        Dim lst = obj As List
        Try
            obj = InnerQuery(lst.Get(key), path)
        Catch
            obj = Null
        End Try
    End If
    Return obj
End Sub

Usage:
B4X:
Dim jsonString As String = $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$
  
  
Dim jsonQ As JSONQuery
jsonQ.Initialize(jsonString)
  
' second param is a default if not found
Log(jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data/field1", "0")) ' returns 1
Log(jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data/field2", "0")) ' returns 2
Log(jsonQ.Query("firstList/doesNotExist/field3", "0")) ' returns 0, the default param

Alwaysbusy
Interesting, will take a look, thanks.
 
Upvote 0

TILogistic

Expert
Licensed User
Longtime User
I use this class to quickly access something in JSON:

class JSONQuery
B4X:
Sub Class_Globals
    Private jsonM As Map
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(jsonString As String)
    Dim jsonP As JSONParser
    jsonP.Initialize(jsonString)
    jsonM = jsonP.NextObject
End Sub

public Sub Query(path As String, default As Object) As Object
    Dim ret As Object = InnerQuery(jsonM, path)
    If ret = Null Then
        ret = default
    End If
    Return ret
End Sub

private Sub InnerQuery(obj As Object, path As String) As Object
    Dim keys() = Regex.Split("/", path) As String : path = ""
    If keys.Length = 0 Then Return obj
    Dim key As String
    For i = 0 To (keys.Length - 1)
        key = keys(i) : If key <> "" Then Exit
    Next
    If key.Length = 0 Then Return obj
    For j = i + 1 To (keys.Length - 1)
        path = path & keys(j) & "/"
    Next
    If obj Is Map Then
        Dim map = obj As Map
        obj = InnerQuery(map.GetDefault(key, Null), path)
    Else If obj Is List Then
        Dim lst = obj As List
        Try
            obj = InnerQuery(lst.Get(key), path)
        Catch
            obj = Null
        End Try
    End If
    Return obj
End Sub

Usage:
B4X:
Dim jsonString As String = $"{
                          "firstList": {
                            "ListInsideFirstList": {
                              "ListInsideSecondList": {
                                "Data": {
                                  "field1": "1",
                                  "field2": "2"
                                }
                              }
                            }
                          }
                        }"$
 
 
Dim jsonQ As JSONQuery
jsonQ.Initialize(jsonString)
 
' second param is a default if not found
Log(jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data/field1", "0")) ' returns 1
Log(jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data/field2", "0")) ' returns 2
Log(jsonQ.Query("firstList/doesNotExist/field3", "0")) ' returns 0, the default param

Dim m As Map = jsonQ.Query("firstList/ListInsideFirstList/ListInsideSecondList/Data", Null)
If m <> Null Then
    For Each key As String In m.Keys
        Log(key & ": " & m.Get(key))
    Next
End If

Alwaysbusy
classes only analyze maps?
Error in JSON List:
JSON:
[
  {
    "firstList": {
      "ListInsideFirstList": {
        "ListInsideSecondList": {
          "Data": {
            "field1": "1",
            "field2": "2"
          }
        }
      }
    }
  }
]

Remember:
 
Last edited:
Upvote 0
Top