0

I have a simple app communicating with its Servlet backend through an Async task. I have some trouble understanding how the messages are wrapped up and how to manipulate the data structures of these messages. What I want to do is to receive either multiple objects, or multiple heterogeneous information anyway. My code:

public class MyServlet extends HttpServlet {
    ArrayList<Tour> m_tours;

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
    resp.setContentType("text/plain");
    resp.getWriter().println("Please use the form to POST to this url");

    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

    String order = req.getParameter("order");
    resp.setContentType("text/plain");
    if (order == null) {
        resp.getWriter().println("Please enter a name");
    }

      resp.getWriter().println("yay name received");
      ArrayList<Tour> m_tours = getTours(); //returns a populated ArrayList of custom Tour objects
      resp.getWriter().print(m_tours);
}
    private void getTours(){
        //some code here
    }
}`

And my Async task class:

class ServletPostAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private Context context;
@Override
protected String doInBackground(Pair<Context, String>... params) {
    context = params[0].first;
    String order = params[0].second;

    String[] url = new String[3];
    url[0] = "http://192.168.169.85:8080/hello";
    url[1] = "http://10.0.2.2:8080/hello";
    url[2] = "http://192.168.1.102:8080/hello";
    HttpClient httpClient = new DefaultHttpClient(); 
    HttpPost httpPost = new HttpPost(url[2]);

    List<NameValuePair> nameValuePairs = new ArrayList<>(1);
    nameValuePairs.add(new BasicNameValuePair("order", order));
        try {
            // Add name data to request
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // Execute HTTP Post Request
            HttpResponse response = httpClient.execute(httpPost);

            HttpEntity entity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200) {
                return EntityUtils.toString(entity);
            }
                return "Error: " + response
                        .getStatusLine()
                        .getStatusCode() + " " + response
                        .getStatusLine().getReasonPhrase();
        } catch (ClientProtocolException e) {
            return e.getMessage();
        } catch (IOException e) {
            return e.getMessage();
        }
    }

@Override
protected void onPostExecute(String result) {
       String result1 = "Response: "+result;
           Toast.makeText(context, result1, Toast.LENGTH_LONG).show();
    }
}

The response message returns ArrayList as text:

 Response: yay name received
 packagename@objectkey1
 packagename@objectkey2
 packagename@objectkey3
 ...
 packagename@objectkeyn

But instead, what I want is to store it as it is, as an ArrayList. How can I configure my Async task to receive my m_tours ArrayList and store it somewhere for further use? Furthermore, how can I configure it to receive multiple objects?

* EDIT *

I've tried by using Gson as suggested by @orip, setting the Async task as follows:

@Override
protected String doInBackground(Pair<Context, String>... params) {
    context = params[0].first;
    String order = params[0].second;

    String[] url = new String[3];
    url[0] = "http://192.168.169.85:8080/hello";
    url[1] = "http://10.0.2.2:8080/hello";
    url[2] = "http://192.168.1.102:8080/hello";
    // HttpPost httpPost = new HttpPost("http://semiotic-art-88319.appspot.com/hello");
    HttpClient httpClient = new DefaultHttpClient(); //127.0.0.1 - 10.201.19.153
    HttpPost httpPost = new HttpPost(url[2]);

    List<NameValuePair> nameValuePairs = new ArrayList<>(1);
    nameValuePairs.add(new BasicNameValuePair("order", order));

    try {
        // Add name data to request
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        // Execute HTTP Post Request
        HttpResponse response = httpClient.execute(httpPost);
        if (response.getStatusLine().getStatusCode() == 200) {
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        }
        return "Error: " + response
                .getStatusLine()
                .getStatusCode() + " " + response
                .getStatusLine().getReasonPhrase();
    } catch (ClientProtocolException e) {
        return e.getMessage();
    } catch (IOException e) {
        return e.getMessage();
    }
}

@Override
protected void onPostExecute(String jsonResponse) {
    Gson gson = new Gson();
    tours = (gson.fromJson(jsonResponse, Tours.class));
    Toast.makeText(context, jsonResponse, Toast.LENGTH_LONG).show();
}

and on the Server side:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

    String asyncMessage = req.getParameter("order");
    if(asyncMessage.equals("tours")){
        m_tours = getTours();  //ArrayList<Tour> m_tours;
        Tours tours = new Tours(m_tours);
        resp.setContentType("application/json");
        PrintWriter out = resp.getWriter();
        out.print(new Gson().toJson(tours));
        out.flush();

        resp.getWriter().print(m_tours);
    }

}

but I get an error:

03-23 13:27:09.523  32387-32387/madapps.bicitourbo E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: madapps.bicitourbo, PID: 32387
com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 692 path $
        at com.google.gson.Gson.assertFullConsumption(Gson.java:786)
        at com.google.gson.Gson.fromJson(Gson.java:776)
        at com.google.gson.Gson.fromJson(Gson.java:724)
        at com.google.gson.Gson.fromJson(Gson.java:696)
        at madapps.bicitourbo.ServletPostAsyncTask.onPostExecute(ServletPostAsyncTask.java:92)
        at madapps.bicitourbo.ServletPostAsyncTask.onPostExecute(ServletPostAsyncTask.java:36)
        at android.os.AsyncTask.finish(AsyncTask.java:632)
        at android.os.AsyncTask.access$600(AsyncTask.java:177)
        at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:149)
        at android.app.ActivityThread.main(ActivityThread.java:5257)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
        at dalvik.system.NativeStart.main(Native Method)
 Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 692 path $

This error occurs on the line:

Tour tours = (gson.fromJson(jsonResponse, Tours.class));

What do I do wrong?

* EDIT2 * Solved:

The error: Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON was due to the fact that I was calling resp.getWriter().print() two times, as suggested by @orip. Thank you!

Cris
  • 1,923
  • 4
  • 25
  • 47

1 Answers1

2

Set the servlet's content type to application/json and return a JSON string (e.g using Gson or Jackson to serialize the result.

On the Android side you can deserialize the JSON string, either using Android's built-in JSON classes or (better) using the same libraries you used in your servlet.

For example, if Tour is something like:

public class Tour {
  // some simple int/string/list fields
}

You can build a response class like:

public class Tours {
  private List<Tour> tours;
  // ...
}

Then on the server side (see this question, I'm using Gson here):

List<Tour> listOfTours = ...;
Tours tours = new Tours(listOfTours);
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.print((new Gson()).toJson(tours));
out.flush();

And on the client side:

String jsonResponse = ...;
Tours tours = (new Gson()).fromJson(jsonResponse, Tours.class);

There are some optimizations to be made, but that could get you started. Also, consider using OkHttp for your HTTP connections instead of using HttpClient, you'll probably end up with simpler and more robust code.

Community
  • 1
  • 1
orip
  • 66,082
  • 20
  • 111
  • 144
  • Thank you for your valuable suggestions! It worked smoothly; but now, in Servlet I am setting the content type to "application/json" just after sending the message "Response: yay message received". Therefore, how do I discriminate, within the response received by the Async task, between the String message and the serialized json in String format? Because I am getting them all together. In short: how do I separate my string from my json? – Cris Mar 22 '15 at 20:47
  • 1
    Return just the json. If you want to add a string field or a success status, add them to the response type (e.g to Tours in my example) – orip Mar 22 '15 at 22:02
  • Ok, done, but I get this error `com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true)` as posted in my last edit. What do I do wrong? – Cris Mar 23 '15 at 16:16
  • You'll need to post the json somewhere so we can see what's wrong (can scrub it manually for privacy if needed). – orip Mar 23 '15 at 19:49
  • Orip, thank you, your suggestions worked. I only have one more question: how can I pass the so created response Tours object containing List list, to the class where I call my Async Task from?? Anytime I try to get that object from the class where I execute the command `ServletPostAsyncTask s = new ServletPostAsyncTask(); s.execute(new Pair(ListViewPrenota.this, "tours"));` I get an error as if the object would be empty... – Cris Mar 24 '15 at 13:15
  • 1
    You get the result in `onPostExecute`, assuming you return the list in `doInBackground`. You can then use it to save in a field, persist to disk, update UI, etc. – orip Mar 24 '15 at 13:36