Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Android

Parsing Data Returned in JSON: "JSONException: Unterminated string at character 5792"

Following along with the videos, I get the error in the title. After doing some research I figured out that the probably cause is getContentLength() is not returning the correct value, thus overflowing the read buffer. From the docs:

By default, this implementation of HttpURLConnection requests that servers use gzip
compression. Since getContentLength() returns the number of bytes transmitted, you
cannot use that method to predict how many bytes can be read from getInputStream().
Instead, read that stream until it is exhausted: when read() returns -1. Gzip
compression can be disabled by setting the acceptable encodings in the request
header:

So, I figured that with gZip compression, the values were getting a little messed up. I went to the URL and saved the raw JSON, then got the character count for that: 5792. Being the same length as the array, there should be no overflow, yet the error show the JSON being cut off in the wrong place. I went ahead and made a paste of the full error, and you can clearly see the mismatch.

I even downloaded the project files and tried it there but still with no luck. Is this an error in the code or am I doing something completely wrong?

2 Answers

Ben Jakuben
STAFF
Ben Jakuben
Treehouse Teacher

This is an error! We discussed this in the Forum a while back and have linked to it as a "Known Issue" in the Teacher Notes on the video page. Check out the solution (and discussion) in that other thread. I will loop back and update the project at some point, too.

Hmm,

I changed the Reader reader deceleration to a type of InputStreamReader and everything seemed to be working more consistently.

After changing that type deceleration back to Reader, the code still works. What exactly is happening to produce this error?

Also, I am using the official treehouse blog, and I was still running into the issue.

Ben Jakuben
Ben Jakuben
Treehouse Teacher

I saw this a while back with the Treehouse Blog, and the solution I linked to above fixed it at that time. Have you tried replacing the whole doInBackground method with this version?:

@Override
protected JSONObject doInBackground(Object... params) {
    int responseCode = -1;
    JSONObject jsonResponse = null;
    StringBuilder builder = new StringBuilder();
    HttpClient client = new DefaultHttpClient();
    HttpGet httpget = new HttpGet("http://www.grepscience.com/api/get_recent_summary/");

    try {
        HttpResponse response = client.execute(httpget);
        StatusLine statusLine = response.getStatusLine();
        responseCode = statusLine.getStatusCode();

        if (responseCode == HttpURLConnection.HTTP_OK) {
            HttpEntity entity = response.getEntity();
            InputStream content = entity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(content));
            String line;
            while((line = reader.readLine()) != null){
                builder.append(line);
            }

            jsonResponse = new JSONObject(builder.toString());
        }
        else {
            Log.i(TAG, String.format("Unsuccessful HTTP response code: %d", responseCode));
        }
    }
    catch (JSONException e) {
        logException(e);
    }
    catch (Exception e) {
        logException(e);
    }           

    return jsonResponse;
} 

Yes, I just did, and now it's working on each attempt without a hitch.

I was just curious as to why it was throwing that error in the first place, sorry if I wasn't terribly clear. Everything is working perfectly fine now :)

Ben Jakuben
Ben Jakuben
Treehouse Teacher

Gotcha - I was never able to figure out exactly what the problem is. It appears to be an issue on the Treehouse Blog when certain special characters are used in titles. At that point I don't know if it's an encoding issue or it somehow breaks the content-length property...

Gerd Hirschmann
Gerd Hirschmann
2,369 Points

This is only for those who are still executing this "deprecated" course and use BenĀ“s code example from above. It want work, you have to change the URL from "grepscience.com" back to "blog.teamtreehouse.com". Grepscience.com returns no JSON data therefore we cannot parse it. If we go back to the blog from teamtreehouse and add the lines from the tutorial and it works like a charm :) Here is my complete code from the "MainListActivity.java" file, feel free to c/p it:

package com.example.blogreader;

import android.app.ListActivity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;


public class MainListActivity extends ListActivity {

    protected String[] mBlogPostTitles;
    public static final int NUMBER_OF_POSTS = 20;
    public static final String TAG = MainListActivity.class.getSimpleName();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_list);

        if (isNetworkAvailable()) {
            GetBlogPostsTask getBlogPostsTask = new GetBlogPostsTask();
            getBlogPostsTask.execute();
        } else {
            Toast.makeText(this, "Network is unavailable!", Toast.LENGTH_LONG).show();
        }



        //Toast.makeText(this, R.string.no_items, Toast.LENGTH_LONG).show();

    }

    private boolean isNetworkAvailable() {
        ConnectivityManager manager = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();

        boolean isAvailable = false;
        if (networkInfo != null && networkInfo.isConnected()) {
            isAvailable = true;
        }
        return isAvailable;
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main_list, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private class GetBlogPostsTask extends AsyncTask<Object, Void, JSONObject> {

        @Override
        protected JSONObject doInBackground(Object... arg0) {
            int responseCode = -1;
            JSONObject jsonResponse = null;
            StringBuilder builder = new StringBuilder();
            HttpClient client = new DefaultHttpClient();
            HttpGet httpget = new HttpGet("http://blog.teamtreehouse.com/api/get_recent_summary?count=" + NUMBER_OF_POSTS);

            try {
                HttpResponse response = client.execute(httpget);
                StatusLine statusLine = response.getStatusLine();
                responseCode = statusLine.getStatusCode();

                if (responseCode == HttpURLConnection.HTTP_OK) {
                    HttpEntity entity = response.getEntity();
                    InputStream content = entity.getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                    String line;
                    while((line = reader.readLine()) != null){
                        builder.append(line);
                    }

                    jsonResponse = new JSONObject(builder.toString());
                    String status = jsonResponse.getString("status");
                    Log.v(TAG, status);

                    JSONArray jsonPosts = jsonResponse.getJSONArray("posts");
                    for (int i = 0; i < jsonPosts.length(); i++) {
                        JSONObject jsonPost = jsonPosts.getJSONObject(i);
                        String title = jsonPost.getString("title");
                        Log.v(TAG, "Post " + i + ": " + title);
                    }

                }
                else {
                    Log.i(TAG, String.format("Unsuccessful HTTP response code: %d", responseCode));
                }
            }
            catch (JSONException e) {
                Log.e(TAG, "Exception; " + e);
            }
            catch (Exception e) {
                Log.e(TAG, "Exception; " + e);
            }
            return null;
        }
    }


}
Michael Dvorscak
Michael Dvorscak
7,003 Points

I had the same issue, and after some investigation the problem seemed to be coming from the HTML escape sequence &#038; (the '&' symbol)

My fix was to instead use OkHttp ; like we previously used in Stormy (Weather App). Here is my updated doInBackground function

//at the top of main activity
private String blogFeedURL = "http://blog.teamtreehouse.com/api/get_recent_summary/?count=" + NUMBER_OF_POSTS;
protected OkHttpClient mClient = new OkHttpClient();
protected Request mRequest = new Request.Builder()
            .url(blogFeedURL)
            .build();
protected JSONObject doInBackground(Object[] params) {
            JSONObject jsonResponse = null;
            try {
                Call call =  mClient.newCall(mRequest);
                //We can use the blocking version since we aren't on the main thread
                Response response = call.execute();

                if (response.isSuccessful()) {
                    String jsonData = response.body().string();
                    jsonResponse = new JSONObject(jsonData);
                } else {
                    Log.d(TAG, "There with the connection");
                }
            } catch (MalformedURLException e) {
                Log.e(TAG, "Exception caught: ", e);
            } catch (IOException e){
                Log.e(TAG, "Exception caught: ", e);
            } catch (JSONException e) {
                Log.e(TAG, "Exception caught: ", e);
            }
            return jsonResponse;
        }

and of course adding: compile 'com.squareup.okhttp:okhttp:2.2.0' to the Gradle dependencies

This was a much cleaner/easier solution.