ThrowingSpoon
ThrowingSpoon

Reputation: 871

Parsing JSON with GSON on Android

I'm trying to parse some JSON generated by the Redmine API with GSON, using this tutorial as a base. Unfortunately I can't give you the URL for my JSON because it only works internally but it looks like this:

{
  "limit": 25,
  "total_count": 653,
  "offset": 0,
  "time_entries": [
    {
      "spent_on": "2013-02-14",
      "activity": {
        "name": "Blah1",
        "id": 10
      },
      "project": {
        "name": "TEST",
        "id": 10
      },
      "id": 661,
      "hours": 1,
      "updated_on": "2013-02-14T14:32:19Z",
      "user": {
        "name": "USER1",
        "id": 7
      },
      "issue": {
        "id": 467
      },
      "comments": "COMMENT 1",
      "created_on": "2013-02-14T14:32:19Z"
    },
    {
      "spent_on": "2013-02-14",
      "activity": {
        "name": "Bla2",
        "id": 10
      },
      "project": {
        "name": "TEST TEST",
        "id": 10
      },
      "id": 660,
      "hours": 1,
      "updated_on": "2013-02-14T11:52:13Z",
      "user": {
        "name": "USER2",
        "id": 4
      },
      "issue": {
        "id": 466
      },
      "comments": "COMMENT 2.",
      "created_on": "2013-02-14T11:52:13Z"
    }
  ]
}

I get a NullPointerException but I'm not sure if it's from failing to get the JSON or modelling it wrong. I'm concerned I'm missing something to do with the inner classes. If it's as simple as the JSON being fetched incorrectly I'll be really annoyed at myself.

My devices are all running with WIFI connected to the internal network and I can see the JSON when I use the browser so I don't think it's a networking issue.

Here Is my LogCat:

02-20 21:11:11.175: E/AndroidRuntime(14450): FATAL EXCEPTION: main
02-20 21:11:11.175: E/AndroidRuntime(14450): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.javacodegeeks.android.json/com.javacodegeeks.android.json.JsonParsingActivity}: java.lang.NullPointerException
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.ActivityThread.access$600(ActivityThread.java:141)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.os.Handler.dispatchMessage(Handler.java:99)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.os.Looper.loop(Looper.java:137)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.ActivityThread.main(ActivityThread.java:5041)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at java.lang.reflect.Method.invokeNative(Native Method)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at java.lang.reflect.Method.invoke(Method.java:511)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at dalvik.system.NativeStart.main(Native Method)
02-20 21:11:11.175: E/AndroidRuntime(14450): Caused by: java.lang.NullPointerException
02-20 21:11:11.175: E/AndroidRuntime(14450):    at com.javacodegeeks.android.json.JsonParsingActivity.onCreate(JsonParsingActivity.java:46)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.Activity.performCreate(Activity.java:5104)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
02-20 21:11:11.175: E/AndroidRuntime(14450):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)

My JsonParsingActivity:

public class JsonParsingActivity extends Activity {

    String url = "URL";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        InputStream source = retrieveStream(url);
        Gson gson = new Gson();
        Reader reader = new InputStreamReader(source);
        RedmineResponse response = gson.fromJson(reader, RedmineResponse.class);

        Toast.makeText(this, response.query, Toast.LENGTH_SHORT).show();

        List<Result> results = response.results;
        for (Result result : results) {
            if (result.comments != null) {
                Toast.makeText(this, result.comments, Toast.LENGTH_SHORT).show();   
            } else {
                Toast.makeText(this, "EMPTY", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private InputStream retrieveStream(String url) {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet getRequest = new HttpGet(url);

        try {
            HttpResponse getResponse = client.execute(getRequest);
            final int statusCode = getResponse.getStatusLine().getStatusCode();

            if (statusCode != HttpStatus.SC_OK) { 
                Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url);
                return null;
            }

            HttpEntity getResponseEntity = getResponse.getEntity();
            return getResponseEntity.getContent();
        } catch (IOException e) {
            getRequest.abort();
            Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
        }

        return null;
    }
}

My Result class:

public class Result {

    @SerializedName("id")
    public String time_entry_ID;

    @SerializedName("spent_on")
    public String spent_on;

    @SerializedName("created_on")
    public String created_on;

    public Project project;

    @SerializedName("hours")
    public String hours;

    @SerializedName("comments")
    public String comments;

    @SerializedName("iso_language_code")
    public String isoLanguageCode;

    @SerializedName("to_user_id_str")
    public String toUserIdStr;

    public String source;
}

My RedmineResponse class:

public class RedmineResponse {

    public List<Result> results;

    @SerializedName("limit")
    public int limit;

    @SerializedName("offset")
    public int offset;

    public String query;
}

My Activity class:

public class Activity {

    @SerializedName("id")
    public String activity_ID;

    @SerializedName("name")
    public String activity_name;
}

My Issue class:

public class Issue {

    @SerializedName("id")
    public String issue_ID;
}

My Project class:

public class Issue {

    @SerializedName("id")
    public String issue_ID;
}

My User class:

public class User {

    @SerializedName("id")
    public String user_ID;

    @SerializedName("name")
    public String user_name;
}

Upvotes: 3

Views: 2618

Answers (1)

Perception
Perception

Reputation: 80598

You need to define the serialized property name for the results collection in your RedmineResponse mapping:

public class RedmineResponse {

    @SerializedName("time_entries") // This is the name you are using in JSON
    public List<Result> results;
}

On a complete tangent, its better to define properties on your Javabeans (aka private variables with getter/setters), because this helps to encapsulate them better.

Upvotes: 3

Related Questions