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!

2
  • 1
    Is resp.getWriter().print(m_tours); still in the code? Should it be? Commented Mar 23, 2015 at 19:50
  • oh right, no it wasn't supposed to be there. I removed it, and now it's working;) Commented Mar 24, 2015 at 13:10

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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?
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)
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?
You'll need to post the json somewhere so we can see what's wrong (can scrub it manually for privacy if needed).
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.