Android Question JSON Array expected

ilan

Expert
Licensed User
Longtime User
hi

i am trying to get the entires from my mysql db via php but what i get is an error saying JSON array expected...

this is the log:

*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
[{"id":"3","title":"דרושים כוונים CNC לכרסום וחריטה","date":"30-07-2019","fav":"0","title2":"גולדברגר טכנולוגיות","jobexp":"לחברת גולדברגר טכנולוגיות הממוקמת בא.ת בר לב (ליד כרמיאל), דרושים כוונים CNC לכרסום וחריטה.\r\n*משרה מלאה.\r\n*אווירה נעימה ואיכותית!\r\n*תנאים טובים מאד למתאימים\/ות.","jobneeds":"- ניסיון קודם - חובה.\r\n- ראש גדול, מוסר עבודה וחריצות.\r\n*המשרה מיועדת לנשים וגברים כאחד.","experience":"1-2","category":"CNC, כרסם - חרט","place":"צפון","period":"משרה מלאה","price":"0","publisher":"גולדברגר","pubemail":"[email protected]","pubid":"1","startdatetime":"00000000000","enddatetime":"00000000000","status":"0"}]
Error occurred on line: 82 (jobs)
java.lang.RuntimeException: JSON Array expected.
at anywheresoftware.b4a.objects.collections.JSONParser.NextArray(JSONParser.java:62)
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:176)
at anywheresoftware.b4a.shell.DebugResumableSub$RemoteResumableSub.resume(DebugResumableSub.java:22)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:250)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:137)
at anywheresoftware.b4a.BA$2.run(BA.java:370)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

this is the code:

B4X:
Sub loadalljobs As ResumableSub
    jobsclv.Clear
   
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download2("http://www.sagital.net/msjobs4.php", Array As String ("action", "getjobs"))
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Log(UnescapeUnicode(j.GetString))
       
        Dim parser As JSONParser
        parser.Initialize(UnescapeUnicode(j.GetString))
        Dim root As List = parser.NextArray
        Log(root.Size)
    End If
    j.Release

    Return True
End Sub


Sub UnescapeUnicode(s As String) As String
    Dim sb As StringBuilder
    sb.Initialize
    Dim i As Int
    Do While i < s.Length
        Dim c As Char = s.CharAt(i)
        If c = "\" And i < s.Length - 1 And s.CharAt(i + 1) = "u" Then
            Dim unicode As StringBuilder
            unicode.Initialize
            i = i + 2
            Do While i < s.Length
                Dim cc As String = s.CharAt(i)
                Dim n As Int = Asc(cc.ToLowerCase)
                If (n >= Asc("0") And n <= Asc("9")) Or (n >= Asc("a") And n <= Asc("f")) Then
                    unicode.Append(s.CharAt(i))
                Else
                    i = i - 1
                    Exit
                End If
                i = i + 1
            Loop
            sb.Append(Chr(Bit.ParseInt(unicode.ToString, 16)))
        Else
            sb.Append(c)
        End If
        i = i + 1
    Loop
    Return sb.ToString
End Sub

and php code:

B4X:
    Case "getjobs":
        $q = mysqli_query($con, "SELECT * FROM $table ORDER BY id");
        $rows = array();
        while($r = mysqli_fetch_assoc($q))
        {
            $rows[] = $r;
        }
        print json_encode($rows);
        mysqli_free_result($q);
    break;

what am i doing wrong?

thanx
 

emexes

Expert
Licensed User
what am i doing wrong?
I don't know, it looks good to me. FWIW, if I run this code in B4J using data downloaded from your website via Chrome browser on a PC:
B4X:
Dim JSONData As String = $"[{"id":"3","title":"\u05d3\u05e8\u05d5\u05e9\u05d9\u05dd \u05db\u05d5\u05d5\u05e0\u05d9\u05dd CNC \u05dc\u05db\u05e8\u05e1\u05d5\u05dd \u05d5\u05d7\u05e8\u05d9\u05d8\u05d4","date":"30-07-2019","fav":"0","title2":"\u05d2\u05d5\u05dc\u05d3\u05d1\u05e8\u05d2\u05e8 \u05d8\u05db\u05e0\u05d5\u05dc\u05d5\u05d2\u05d9\u05d5\u05ea","jobexp":"\u05dc\u05d7\u05d1\u05e8\u05ea \u05d2\u05d5\u05dc\u05d3\u05d1\u05e8\u05d2\u05e8 \u05d8\u05db\u05e0\u05d5\u05dc\u05d5\u05d2\u05d9\u05d5\u05ea \u05d4\u05de\u05de\u05d5\u05e7\u05de\u05ea \u05d1\u05d0.\u05ea \u05d1\u05e8 \u05dc\u05d1 (\u05dc\u05d9\u05d3 \u05db\u05e8\u05de\u05d9\u05d0\u05dc), \u05d3\u05e8\u05d5\u05e9\u05d9\u05dd \u05db\u05d5\u05d5\u05e0\u05d9\u05dd CNC \u05dc\u05db\u05e8\u05e1\u05d5\u05dd \u05d5\u05d7\u05e8\u05d9\u05d8\u05d4.\r\n*\u05de\u05e9\u05e8\u05d4 \u05de\u05dc\u05d0\u05d4.\r\n*\u05d0\u05d5\u05d5\u05d9\u05e8\u05d4 \u05e0\u05e2\u05d9\u05de\u05d4 \u05d5\u05d0\u05d9\u05db\u05d5\u05ea\u05d9\u05ea!\r\n*\u05ea\u05e0\u05d0\u05d9\u05dd \u05d8\u05d5\u05d1\u05d9\u05dd \u05de\u05d0\u05d3 \u05dc\u05de\u05ea\u05d0\u05d9\u05de\u05d9\u05dd.","jobneeds":"- \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05e7\u05d5\u05d3\u05dd - \u05d7\u05d5\u05d1\u05d4.\r\n- \u05e8\u05d0\u05e9 \u05d2\u05d3\u05d5\u05dc, \u05de\u05d5\u05e1\u05e8 \u05e2\u05d1\u05d5\u05d3\u05d4 \u05d5\u05d7\u05e8\u05d9\u05e6\u05d5\u05ea.\r\n*\u05d4\u05de\u05e9\u05e8\u05d4 \u05de\u05d9\u05d5\u05e2\u05d3\u05ea \u05dc\u05e0\u05e9\u05d9\u05dd \u05d5\u05d2\u05d1\u05e8\u05d9\u05dd \u05db\u05d0\u05d7\u05d3.","experience":"1-2","category":"CNC, \u05db\u05e8\u05e1\u05dd - \u05d7\u05e8\u05d8","place":"\u05e6\u05e4\u05d5\u05df","period":"\u05de\u05e9\u05e8\u05d4 \u05de\u05dc\u05d0\u05d4","price":"0","publisher":"\u05d2\u05d5\u05dc\u05d3\u05d1\u05e8\u05d2\u05e8","pubemail":"[email protected]","pubid":"1","startdatetime":"00000000000","enddatetime":"00000000000","status":"0"}]"$
Log(JSONData.Length)
Log("|" & JSONData & "|")

Dim JSONDataUnicode As String = UnescapeUnicode(JSONData)
Log(JSONDataUnicode.Length)
Log("|" & JSONDataUnicode & "|")

Dim parser As JSONParser
parser.Initialize(JSONDataUnicode)
Dim root As List = parser.NextArray
Log(root.Size)
Dim branch As Object = root.Get(0)
Log(branch)
then I get this log:
B4X:
1877
|[{"id":"3","title":"\u05d3\u05e8\u05d5\u05e9\u05d9\u05dd \u05db\u05d5\u05d5\u05e0\u05d9\u05dd CNC \u05dc\u05db\u05e8\u05e1\u05d5\u05dd \u05d5\u05d7\u05e8\u05d9\u05d8\u05d4","date":"30-07-2019","fav":"0","title2":"\u05d2\u05d5\u05dc\u05d3\u05d1\u05e8\u05d2\u05e8 \u05d8\u05db\u05e0\u05d5\u05dc\u05d5\u05d2\u05d9\u05d5\u05ea","jobexp":"\u05dc\u05d7\u05d1\u05e8\u05ea \u05d2\u05d5\u05dc\u05d3\u05d1\u05e8\u05d2\u05e8 \u05d8\u05db\u05e0\u05d5\u05dc\u05d5\u05d2\u05d9\u05d5\u05ea \u05d4\u05de\u05de\u05d5\u05e7\u05de\u05ea \u05d1\u05d0.\u05ea \u05d1\u05e8 \u05dc\u05d1 (\u05dc\u05d9\u05d3 \u05db\u05e8\u05de\u05d9\u05d0\u05dc), \u05d3\u05e8\u05d5\u05e9\u05d9\u05dd \u05db\u05d5\u05d5\u05e0\u05d9\u05dd CNC \u05dc\u05db\u05e8\u05e1\u05d5\u05dd \u05d5\u05d7\u05e8\u05d9\u05d8\u05d4.\r\n*\u05de\u05e9\u05e8\u05d4 \u05de\u05dc\u05d0\u05d4.\r\n*\u05d0\u05d5\u05d5\u05d9\u05e8\u05d4 \u05e0\u05e2\u05d9\u05de\u05d4 \u05d5\u05d0\u05d9\u05db\u05d5\u05ea\u05d9\u05ea!\r\n*\u05ea\u05e0\u05d0\u05d9\u05dd \u05d8\u05...
627
|[{"id":"3","title":"דרושים כוונים CNC לכרסום וחריטה","date":"30-07-2019","fav":"0","title2":"גולדברגר טכנולוגיות","jobexp":"לחברת גולדברגר טכנולוגיות הממוקמת בא.ת בר לב (ליד כרמיאל), דרושים כוונים CNC לכרסום וחריטה.\r\n*משרה מלאה.\r\n*אווירה נעימה ואיכותית!\r\n*תנאים טובים מאד למתאימים.","jobneeds":"- ניסיון קודם - חובה.\r\n- ראש גדול, מוסר עבודה וחריצות.\r\n*המשרה מיועדת לנשים וגברים כאחד.","experience":"1-2","category":"CNC, כרסם - חרט","place":"צפון","period":"משרה מלאה","price":"0","publisher":"גולדברגר","pubemail":"[email protected]","pubid":"1","startdatetime":"00000000000","enddatetime":"00000000000","status":"0"}]|
1
{date=30-07-2019, period=משרה מלאה, [email protected], jobneeds=- ניסיון קודם - חובה.
- ראש גדול, מוסר עבודה וחריצות.
*המשרה מיועדת לנשים וגברים כאחד., title2=גולדברגר טכנולוגיות, jobexp=לחברת גולדברגר טכנולוגיות הממוקמת בא.ת בר לב (ליד כרמיאל), דרושים כוונים CNC לכרסום וחריטה.
*משרה מלאה.
*אווירה נעימה ואיכותית!
*תנאים טובים מאד למתאימים., title=דרושים כוונים CNC לכרסום וחריטה, experience=1-2, price=0, pubid=1, enddatetime=00000000000, publisher=גולדברגר, fav=0, id=3, place=צפון, category=CNC, כרסם - חרט, startdatetime=00000000000, status=0}
 
Upvote 0

amidgeha

Active Member
Licensed User
Longtime User
hi

i am trying to get the entires from my mysql db via php but what i get is an error saying JSON array expected...

this is the log:



this is the code:

B4X:
Sub loadalljobs As ResumableSub
    jobsclv.Clear
  
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download2("http://www.sagital.net/msjobs4.php", Array As String ("action", "getjobs"))
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Log(UnescapeUnicode(j.GetString))
      
        Dim parser As JSONParser
        parser.Initialize(UnescapeUnicode(j.GetString))
        Dim root As List = parser.NextArray
        Log(root.Size)
    End If
    j.Release

    Return True
End Sub


Sub UnescapeUnicode(s As String) As String
    Dim sb As StringBuilder
    sb.Initialize
    Dim i As Int
    Do While i < s.Length
        Dim c As Char = s.CharAt(i)
        If c = "\" And i < s.Length - 1 And s.CharAt(i + 1) = "u" Then
            Dim unicode As StringBuilder
            unicode.Initialize
            i = i + 2
            Do While i < s.Length
                Dim cc As String = s.CharAt(i)
                Dim n As Int = Asc(cc.ToLowerCase)
                If (n >= Asc("0") And n <= Asc("9")) Or (n >= Asc("a") And n <= Asc("f")) Then
                    unicode.Append(s.CharAt(i))
                Else
                    i = i - 1
                    Exit
                End If
                i = i + 1
            Loop
            sb.Append(Chr(Bit.ParseInt(unicode.ToString, 16)))
        Else
            sb.Append(c)
        End If
        i = i + 1
    Loop
    Return sb.ToString
End Sub

and php code:

B4X:
    Case "getjobs":
        $q = mysqli_query($con, "SELECT * FROM $table ORDER BY id");
        $rows = array();
        while($r = mysqli_fetch_assoc($q))
        {
            $rows[] = $r;
        }
        print json_encode($rows);
        mysqli_free_result($q);
    break;

what am i doing wrong?

thanx

Invalid character at the very first character, I deleted [ and retyped [ then it works
go to
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
Yes, it works your way. to prove it I used this and this I just deleted the [ character and the retyped it then all is ok
SCREENSHOT

thank you very much, i will try it when i get home.
so all i need is to remove the [] chars before parsing it via json parser?
 
Upvote 0

amidgeha

Active Member
Licensed User
Longtime User
thank you very much, i will try it when i get home.
so all i need is to remove the [] chars before parsing it via json parser?
No, I just wanted to point out that there is something before the [ character . Note that Hebrew is double byte character language
 
Upvote 0

emexes

Expert
Licensed User
Perhaps it is a byte order mark. It is a bit unkind that it isn't removed for you when the return result is converted to a string (and presumably this is why I didn't experience the problem: Chrome cleaned it off for me).

Simplest solution is probably to do a .SubString from the "[" onwards.
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
B4X:
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download2("http://sagital.net.cp-25.webhostbox.net/" & "msjobs.php", Array As String ("action", "getjobs"))
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Dim str As String = UnescapeUnicode(j.GetString)
        Log(str)
        
        Dim size As Int = str.IndexOf2("[",0)
        Log(size)

'        Dim parser As JSONParser
'        parser.Initialize(str)
'        Dim root As List = parser.NextArray
'        Log(root.Size)
'        Dim branch As Object = root.Get(0)
'        Log(branch)
        
    End If
    j.Release

logs:

*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
[{"id":"3","title":"דרושים כוונים CNC לכרסום וחריטה","date":"30-07-2019","fav":"0","title2":"גולדברגר טכנולוגיות","jobexp":"לחברת גולדברגר טכנולוגיות הממוקמת בא.ת בר לב (ליד כרמיאל), דרושים כוונים CNC לכרסום וחריטה.\r\n*משרה מלאה.\r\n*אווירה נעימה ואיכותית!\r\n*תנאים טובים מאד למתאימים.","jobneeds":"- ניסיון קודם - חובה.\r\n- ראש גדול, מוסר עבודה וחריצות.\r\n*המשרה מיועדת לנשים וגברים כאחד.","experience":"1-2","category":"CNC, כרסם - חרט","place":"צפון","period":"משרה מלאה","price":"0","publisher":"גולדברגר","pubemail":"[email protected]","pubid":"1","startdatetime":"00000000000","enddatetime":"00000000000","status":"0"}]
2

you are right. there are 2 chars (invisible chars) that are before the char [ what breaks the JSON parsing. really weird. what could be the reason for that?
is something in my PHP code wrong?

I will probably do the str.indexof2() to remove everything before [ but why is that happening?

thanx
 
Upvote 0

emexes

Expert
Licensed User
The first two characters of the returned data are Byte Order Marks = Unicode 0xFEFF = three bytes EF BB BF when encoded by UTF-8. Why there are two of them is a bit of mystery - I am guessing there were some Unicode decoding problems in the pipeline back in the early days, and each time BOMs were added to fix the problem.

Or perhaps it was a cautious programmer having a bad day, who added two of them to be sure, to be sure ;-)

upload_2019-8-6_8-1-29.png


Personally I think they should be removed when the string comes out the other end of the UTF-8 pipeline and is converted back into discrete atomic Unicode characters where byte order has no meaning.

Having thought a bit harder, a simpler way to remove single and multiple BOMs is probably:
B4X:
'''Dim DirtyString As String = UnescapeUnicode(j.GetString)
Dim CleanString As String = UnescapeUnicode(j.GetString).Replace(Chr(0xFEFF), "")    'remove BOMs if present
although this will remove them from everywhere in the string, not just from at the beginning - but this is probably a good thing.
 
Last edited:
Upvote 0

ilan

Expert
Licensed User
Longtime User
thanx, a lot @emexes for the explanation. I encounter another issue that I had a long time ago but did not know how to fix. I googled a lot but all the suggestions did not help. my problem is that after I update my MySQL DB with a new entry and I ask to receive all entries back I don't get the new entries. if I change the name of the PHP file in my FTP I get the new entries. if I wait for a while I also get the new entries so I think there is a cache file that loads the old entries but I am not sure. how can I always get immediately the new entries without renaming my PHP file or waiting a few minutes?

I read this but I cant find the php.ini file https://stackoverflow.com/questions/11372704/php-file-will-not-update-in-browser
 
Upvote 0

emexes

Expert
Licensed User
how can I always get immediately the new entries without renaming my PHP file or waiting a few minutes?
I have a theory, went to test it using your server, got this:

upload_2019-8-6_11-50-9.png


If you could restore that php test page, that'd be great. Better yet, set up another test page that returns changing information - doesn't matter what: random, counter, current time all good.
 
Upvote 0

Brandsum

Well-Known Member
Licensed User
if I wait for a while I also get the new entries so I think there is a cache file that loads the old entries
Yes that is a problem of web api. Either you can set no cache header at the very first line of your php file or you can send DateTime.Now as an extra query parameter to overcome this issue.
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
I have a theory, went to test it using your server, got this:

View attachment 82889

If you could restore that php test page, that'd be great. Better yet, set up another test page that returns changing information - doesn't matter what: random, counter, current time all good.

i have renamed the file when i was testing yesterday
this is the right link: http://www.sagital.net/msjobs.php?action=getjobs


. Either you can set no cache header at the very first line of your php file

can you please give me tell me what i should write ? and also where. i am a big noob with php.

thank you very much :)
 
Upvote 0

Brandsum

Well-Known Member
Licensed User
can you please give me tell me what i should write ?
PHP:
<?php
header("Content-Type: application/json");
header("Expires: on, 01 Jan 1970 00:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

//your code

?>
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
PHP:
<?php
header("Content-Type: application/json");
header("Expires: on, 01 Jan 1970 00:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

//your code

?>

thank you very much for your help and of course a big thanx to all members here. (great community :) )
 
Upvote 0

emexes

Expert
Licensed User
i have renamed the file when i was testing yesterday
this is the right link: http://www.sagital.net/msjobs.php?action=getjobs
Where I was heading was that http requests are often cached at various places on the path from the source server to end user client or browser.

I'm pretty sure that usually that caching is keyed on the URL, so all that should be necessary to bypass it is to request a different URL. I was going to just use a random numberm but this:
or you can send DateTime.Now as an extra query parameter to overcome this issue.
is probably a nicer solution, could be useful for debugging too, eg, in your server logs of http request, you'll get an idea of how long the request took to get from the client to the server.

Anyway, I just tried:

http://www.sagital.net/msjobs.php?action=getjobs&random=123554

and it returned the same data as without the extra random parameter, so that's a start.

I did expect to see some cache-timeout line in the http header, but... there doesn't seem to be any (Age looks like a 3-minute countdown?):
B4X:
HTTP/1.1 200 OK
Date: Tue, 06 Au019 07:11:53 GMT
Server: Apache/2.4.39 (cPanel) OpenSSL/1.0.2r mod_bwlimited/1.4 Phusion_Passenger/5.3.7
X-Powered-By: PHP/5.4.45
Vary: Accept-Encoding
Content-Type: text/html
X-Varnish: 229642900 224812425
Age: 172
Via: 1.1 varnish-v4
Accept-Ranges: bytes
Content-Length: 10104
Connection: keep-alive

[{"id":"3","title":"\u05d3\u05e8\u05d5\u05e9\u05d9\u05dd \u05db\ ...
 
Last edited:
Upvote 0

ilan

Expert
Licensed User
Longtime User
and it returned the same data as without the extra random parameter, so that's a start.

so it is cached as i assumed. i will try the PHP snipped that @Brandsum posted and will see if it is updated immediately. thanx again for all your help and tests :)
 
Upvote 0
Top