I would like to be able to determine if a JSON string will be parseable as a LIST or as a MAP before processing it.
Am I missing something, or is the only way to know is to test the first character?
"[" = LIST
"{" = MAP
code:
Dim json As JSONParser
json.Initialize(StdOut)
If json.IsInitialized Then
If StdOut.StartsWith("[") Then
For Each Program As Map In json.NextArray
'processing as a list of map
Next
Else
Dim Program As Map = json.NextObject
'processing as a map
End If
End If
It's a way (selecting between "[" or "{")... if you have only between those two to "select"..
B4X:
Dim list1 As List
list1.Initialize
list1.Add("AAA")
list1.Add("BBB")
Dim map1 As Map
map1.Initialize
map1.Put("field1","aaa")
map1.Put("field1","bbb")
Dim list2 As List
list2.Initialize
list2.Add(map1)
Log("this is the list")
Log(list1.As(JSON).ToString)
Log("this is the map")
Log(map1.As(JSON).ToString)
Log("this is the 2nd list")
Log(list2.As(JSON).ToString)
ps: I ve create this to remember - how starting...?
I am sure that you asking because you want to pass back the values from string to list or map... right ?
Dim aaa As String=map1.As(JSON).ToString
Dim tryit As Boolean=False
Try
Dim listormap1 As List=aaa.As(JSON).tolist
Log ("it is a list...")
Catch
tryit=True
Log ("it isn't list...")
End Try
If tryit=True Then
Try
tryit=False
Dim listormap2 As Map=aaa.As(JSON).Tomap
Log ("it is a map...")
Catch
tryit=True
Log ("it isn't map...")
End Try
End If
If tryit=True Then Log("It is something different")
Sub AppStart (Args() As String)
Dim str1 As String = $"{"a": 3}"$
Log( "str1 is Map? " & IsMap(str1) )
Log( "str1 is List? " & IsList(str1) )
Dim str2 As String = $"["a", 3]"$
Log( "str2 is Map? " & IsMap(str2) )
Log( "str2 is List? " & IsList(str2) )
Dim str3 As String = $"{"x": ["a", 3]}"$
Log( "str3 is Map? " & IsMap(str3) )
Log( "str3 is List? " & IsList(str3) )
Dim str4 As String = $"[{"a": 2}, {"3": "y"}]"$
Log( "str4 is Map? " & IsMap(str4) )
Log( "str4 is List? " & IsList(str4) )
End Sub
Sub IsMap (str As String) As Boolean
Try
Dim M As Map = str.As(JSON).ToMap
If M.IsInitialized Then
'Log("Map initialized")
'Log(M.Size)
Return True
End If
Catch
'Log(LastException)
Return False
End Try
Return False
End Sub
Sub IsList (str As String) As Boolean
Try
Dim L As List = str.As(JSON).ToList
If L.IsInitialized Then
'Log("List initialized")
'Log(L.Size)
Return True
End If
Catch
'Log(LastException)
Return False
End Try
Return False
End Sub
Bash:
str1 is Map? true
str1 is List? false
str2 is Map? false
str2 is List? true
str3 is Map? true
str3 is List? false
str4 is Map? false
str4 is List? true
Sub IsMap (str As String) As Boolean
Try
Dim M As Map = str.As(JSON).ToMap
Return M.IsInitialized
Catch
Return False
End Try
End Sub
Sub IsList (str As String) As Boolean
Try
Dim L As List = str.As(JSON).ToList
Return L.IsInitialized
Catch
Return False
End Try
End Sub
B4X:
Dim str5 As String = "{}"
Log( "str5 is Map? " & IsMap(str5) )
Log( "str5 is List? " & IsList(str5) )
Dim str6 As String = "[]"
Log( "str6 is Map? " & IsMap(str6) )
Log( "str6 is List? " & IsList(str6) )
Bash:
str5 is Map? true
str5 is List? false
str6 is Map? false
str6 is List? true
Dim str7 As String = "{,}"
Log( "str7 is Map? " & IsMap(str7) )
Dim str8 As String = "{:}"
Log( "str8 is Map? " & IsMap(str8) )
Dim str9 As String = "[,]"
Log( "str9 is List? " & IsList(str9) )
Dim str10 As String = "[:]"
Log( "str10 is List? " & IsList(str10) )
Bash:
str7 is Map? false
str8 is Map? false
str9 is List? false
str10 is List? false
Thanks for your answers. I always thought it was a bad idea to have many try/catches as it was inefficient?
The code that I'm running is a jshell that executes a PowerShell every 5 seconds (gets a list of processes). The output of this PowerShell is always a JSON but sometimes it's a list of maps because the PowerShell found many items, and sometimes it's a map because it found only a single item.
Because it's running every 5 seconds I was looking for the most efficient way of coding that.
I think that so far, testing the first character is the simplest, but I'm wondering if it's bullet proof
I don’t think so. I used to think the same when I started with B4X and JSON but after long time understanding json I think I was wrong.
You may have string with single { only and many other invalid patterns.
Public Sub IsJson(Json As String) As Boolean
Dim Pattern As String = $"[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}"$
Return Regex.IsMatch(Pattern, Json.Trim) And Json.Length > 2
End Sub
And
and validate the first and last characters of the json string for list or map
or use Try cash
The output of this PowerShell is always a JSON but sometimes it's a list of maps because the PowerShell found many items, and sometimes it's a map because it found only a single item.
The output of this PowerShell is always a JSON but sometimes it's a list of maps because the PowerShell found many items, and sometimes it's a map because it found only a single item.
Dim S As String = JsonFromPowerShell
Log(S)
S = S.Trim
If S.Length = 0 Then
Log("json? what json?")
ElseIf S.StartsWith("[") and S.EndsWith("]") Then 'to be sure, to be sure ?
'no worries, is already array (containing multiple elements)
ElseIf S.StartsWith("{") And S.EndsWith("}") Then
S = "[" & S & "]" 'convert single element into array (containing that one element)
Else
Log("don't look like no JSON to me")
End If
Dim jp As JSONParser
jp.Initialize(S)
Dim jpl As List
jpl.Initialize 'probably not necessary (if S.Length <> 0) but... slow and steady, wins the race ?
If S.Length <> 0 Then 'may as well make it work for zero-element case too
jpl = jp.NextArray
End If
Log(jpl)
The output of this PowerShell is always a JSON but sometimes it's a list of maps because the PowerShell found many items, and sometimes it's a map because it found only a single item.
Public Sub IsJson(Json As String) As Boolean
Dim Pattern As String = $"[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}"$
Return Regex.IsMatch(Pattern, Json.Trim) And Json.Length > 2
End Sub
2.- Process the Json (List or Map)
Note:
"[]" = LIST
"{}" = MAP
B4X:
'This json string when converted is transformed into map
Dim sJson As String = $"{"Monday": 2, "Thursday": 5, "Friday": 6, "Sunday": 1, "Wednesday": 4, "Tuesday": 3, "Saturday": 7 }"$
'You can convert to Map or List (Example to List)
Dim IsLIst As Boolean = IIf(sJson.StartsWith("[") And sJson.EndsWith("]"), True, False)
Dim lstJson As List = IIf(IsLIst, sJson.As(JSON).ToList, ("[" & sJson & "]").As(JSON).ToList)
Log(lstJson.As(JSON).ToString)
@TILogistic I do like your idea of encapsulating the json string between "[" "]". If you would do it always, no matter the json string (still needs to be valid of course), you are sure that you can always use .NextArray as the first call.
B4X:
Dim sJson As String = $"[{"test": "test"}]"$
sJson = "[" & sJson & "]"
Dim json As JSONParser
json.Initialize(sJson)
dim lst as List = json.NextArray
dim obj as Object = lst.get(0)
if obj is Map then
end if
if obj is List then
end if
EDIT: just read that was already suggested by @emexes
Hi, I would like to be able to determine if a JSON string will be parseable as a LIST or as a MAP before processing it. Am I missing something, or is the only way to know is to test the first character? "[" = LIST "{" = MAP Dim json As JSONParser json.Initialize(StdOut) If...
Dim sJson As String = $"[{"Monday": 2, "Thursday": 5, "Friday": 6, "Sunday": 1, "Wednesday": 4, "Tuesday": 3, "Saturday": 7 }"$
Try
'You can convert to Map or List (Example to List)
Dim IsLIst As Boolean = IIf(sJson.StartsWith("[") And sJson.EndsWith("]"), True, False)
Dim lstJson As List = IIf(IsLIst, sJson.As(JSON).ToList, ("[" & sJson & "]").As(JSON).ToList)
Log(lstJson.As(JSON).ToString)
Catch
Log("Error: Json Not Valid")
End Try
That is how you look at it I guess. If it is an error, in my view, it can not be valid so a 'valid error' does not exist, it is just an error and badly formed Json. That is why you need the try catch.