• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Parsing trivial JSON stream in Android

RhinoCan

Well-Known Member
My Android app is using AsyncTask to invoke a PHP script that accesses a MySQL database. The PHP script deletes a row out of a table and returns the number of rows deleted. It's important that I look at that number: the delete should *always* delete exactly one row so if I get any other number, especially 0, I need to know so that I can respond appropriately.

I am imitating an example that fetches data from a query and returns it to Android via a JSON stream. Now, I should state clearly that I do NOT insist on a solution that uses JSON although I gather it is a very common way of doing things, which is fine by me. I like to learn new things and widely used new things are especially likely to be useful down the road. However, I want to emphasize that I *am* open to other ways of passing data from the PHP script to Android.

I've never had occasion to work with JSON before so forgive me if any of these questions seem particularly silly ;-)

I am NOT clear on exactly how JSON needs to be formatted but having skimmed through some tutorials, I gather that JSON objects appear within braces and JSON arrays appear within square brackets. The PHP code for my delete routine is presently writing this (and only this, aside from the header):
Code:
{RowsDeleted:1}

Now, I do NOT know if that is acceptable output from JSON or if I actually need that object within square brackets to make it an array like this:
Code:
[{RowsDeleted:1}]


I'm also not sure if I need *any* brackets or braces and could simply send back a 0 by itself so that's the first thing to sort out in the PHP code. The second thing is the header. The example I'm copying sends this header:
PHP:
 header('Content-Type:Application/json');
However, when I skimmed the official JSON API it says I should ONLY send:
PHP:
Content-Type: application/vnd.api+json
without any media type parameters.

Most of the examples I've seen do NOT send the header specified in the API documentation so I'm not sure if that is important or it's just a better practice to send the header in the API.

Now, on to the client (Android) side. Here is the code I have so far, which is NOT working:
Java:
   public void deleteSale(int position, String clientCode, String orderNumber) {

        AsyncTask<Integer, Void, Void> asyncTask = new AsyncTask<Integer, Void, Void>() {
            @Override
            protected Void doInBackground(Integer... movieIds) {

                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url(DATABASE_PATH + "deleteOneTWSale.php?clientCode=" + clientCode + "&orderNumber=" + orderNumber)
                        .build();
                try {
                    Response response = client.newCall(request).execute();

                    JSONArray array = new JSONArray(response.body().string());

                    for (int ix = 0; ix < array.length(); ix++) {

                        JSONObject object = array.getJSONObject(ix);
                        String rowsDeleted = object.getString("RowsDeleted");

                        int rowCount = Integer.parseInt(rowsDeleted);

                        if (rowCount != 1) {
                            Log.e(">>ListSales", "Failed to delete desired sale");
                        }


                        ListSales.this.mSalesList.remove(position);
                    }

                } catch (IOException excp) {
                    excp.printStackTrace();
                    Log.e(">>ListSales", "IOException", excp);
                }
                catch (JSONException excp) {
                    excp.printStackTrace();
                    Log.e(">>ListSales", "JSONException", excp);
                }


                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                mAdapter.notifyItemRemoved(position);
            }
        };

        asyncTask.execute();
        return;
    }

My code is throwing a JSON exception when I try to execute this line:
Java:
JSONArray array = new JSONArray(response.body().string());

The stackTrace says:
Java:
07-23 20:11:07.158 23914-25414/com.example.android.twlistsales E/>>ListSales: JSONException
    org.json.JSONException: Value {"RowsDeleted":0} of type org.json.JSONObject cannot be converted to JSONArray
        at org.json.JSON.typeMismatch(JSON.java:111)
        at org.json.JSONArray.<init>(JSONArray.java:96)
        at org.json.JSONArray.<init>(JSONArray.java:108)
        at com.example.android.twlistsales.ListSales$4.doInBackground(ListSales.java:407)
        at com.example.android.twlistsales.ListSales$4.doInBackground(ListSales.java:396)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)

It's pretty clear I'm not parsing the JSON stream properly so that's why I posted. I'm guessing it's because my JSON stream isn't properly written at the PHP side and that if I just fix *that* up, things will be simpler on the Java/Android side. (I certainly expect to dispense with the loop for going through the array given that there is so little coming back in this stream.)

I've tried several variations on the Java code but nothing works. I'm hoping someone here can guide me on what I have to change.
 
First question is, why do you need to represent the result as an array? If it's just a simple integer value, use a JSON data structure like this:

Code:
{
  "rowsDeleted" : 1
}
 
First question is, why do you need to represent the result as an array? If it's just a simple integer value, use a JSON data structure like this:

Code:
{
  "rowsDeleted" : 1
}
I am absolutely fine with passing back only what you've suggested; in fact, that's exactly what I'm doing for now (pending advice to the contrary from someone more knowledgeable). I just wanted to be sure that was the RIGHT thing to pass in the JSON stream. I'm glad to hear that I seem to have reasoned things out correctly so as to avoid passing back more than I needed.

Are the spaces and newlines in the stream important? In other words is this:
Code:
{"RowsDeleted":1}
any different than
Code:
{
  "RowsDeleted" : 1
}
this? My stream *does* include the newlines and blanks as you've shown them; I'm just curious if I can accidentally break things by omitting spaces and newlines (assuming that's even possible in the PHP code).

Also, is the header an issue? I'm guessing it's not given all the advice I've seen where people are sending different headers, rather than the one demanded by the API.

Okay then, assuming the spaces and newlines and headers aren't a problem, I took another whack at my Java code and eventually came up with this, which seems to parse correctly:

Java:
Response response = client.newCall(request).execute();
JSONObject object = new JSONObject(response.body().string());

int rowCount = Integer.parseInt(object.getString("RowsDeleted"));

if (rowCount != 1) {
    Log.e(">>ListSales", "Failed to delete sale for client code " + clientCode + " and order number " + orderNumber + ". Rows deleted = " + rowCount);
    return null;
}

I just have to figure out the last important thing: how to change the data in the ArrayList in a corresponding way. That should be easy. But I think I need to spend a bit of time reading about AsyncTask, including the onPostExecute() method to better understand how they work. I think I want to do my delete in the ArrayList in onPostExecute() but I need to do that delete CONDITIONALLY since I don't want the item deleted from the ArrayList if the delete failed in the database, otherwise everything will get totally out of sync....
 
Back
Top Bottom