Paymahn Moghadasian
Paymahn Moghadasian

Reputation: 10359

JSON Parsing and Robospice

I'm building an Android app using Robospice for communication over network. The resulting object returned from my server does not get deserialized properly. Here are the related classes:

UserRequest.java

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.json.jackson.JacksonFactory;
import com.octo.android.robospice.request.googlehttpclient.GoogleHttpClientSpiceRequest;

import roboguice.util.temp.Ln;
import trees.park.cal.smoke.models.UserList;

public class UserRequest extends GoogleHttpClientSpiceRequest<UserList>{

    private String baseUrl = "http://10.0.0.3:8181/users/";

    public UserRequest() {
        super(UserList.class);
    }

    @Override
    public UserList loadDataFromNetwork() throws Exception {
        Ln.d("Call web service " + baseUrl);
        HttpRequest request = getHttpRequestFactory()
                .buildGetRequest(new GenericUrl(baseUrl));
        request.setParser( new JacksonFactory().createJsonObjectParser() );
        HttpResponse response = request.execute();
        return response.parseAs( getResultType() );
    }
}

UserList.java

import java.util.ArrayList;

public class UserList extends ArrayList<User> {
}

User.java

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Collections;
import java.util.List;

public class User {

    @JsonProperty("id")private long id;
    @JsonProperty("email")private String email;
    @JsonProperty("password")private String password;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @JsonCreator
    public User(@JsonProperty("id") long id, @JsonProperty("email") String email, @JsonProperty("password") String password) {
        this.id = id;
        this.email = email;
        this.password = password;
    }

}

When response.parseAs(getRequestType()) is called, an exception is raised with the following stack trace:

08-19 23:54:08.411  27117-27139/trees.park.cal.smoke E//DefaultRequestRunner.java:154﹕ 23:54:08.418 Thread-5533 An exception occurred during request network execution :
    java.lang.IllegalArgumentException:
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:381)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:354)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:87)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81)
            at com.google.api.client.http.HttpResponse.parseAs(HttpResponse.java:459)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:27)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:12)
            at com.octo.android.robospice.request.CachedSpiceRequest.loadDataFromNetwork(CachedSpiceRequest.java:48)
            at com.octo.android.robospice.request.DefaultRequestRunner.processRequest(DefaultRequestRunner.java:150)
            at com.octo.android.robospice.request.DefaultRequestRunner$1.run(DefaultRequestRunner.java:217)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:841)
     Caused by: java.lang.IllegalArgumentException:
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880)
            at com.google.api.client.json.JsonParser.parseArray(JsonParser.java:647)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:739)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:381)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:354)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:87)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81)
            at com.google.api.client.http.HttpResponse.parseAs(HttpResponse.java:459)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:27)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:12)
            at com.octo.android.robospice.request.CachedSpiceRequest.loadDataFromNetwork(CachedSpiceRequest.java:48)
            at com.octo.android.robospice.request.DefaultRequestRunner.processRequest(DefaultRequestRunner.java:150)
            at com.octo.android.robospice.request.DefaultRequestRunner$1.run(DefaultRequestRunner.java:217)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:841)
     Caused by: java.lang.IllegalArgumentException: unable to create new instance of class trees.park.cal.smoke.models.User because it has no accessible default constructor
            at com.google.api.client.util.Types.handleExceptionForNewInstance(Types.java:165)
            at com.google.api.client.util.Types.newInstance(Types.java:120)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:763)
            at com.google.api.client.json.JsonParser.parseArray(JsonParser.java:647)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:739)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:381)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:354)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:87)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81)
            at com.google.api.client.http.HttpResponse.parseAs(HttpResponse.java:459)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:27)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:12)
            at com.octo.android.robospice.request.CachedSpiceRequest.loadDataFromNetwork(CachedSpiceRequest.java:48)
            at com.octo.android.robospice.request.DefaultRequestRunner.processRequest(DefaultRequestRunner.java:150)
            at com.octo.android.robospice.request.DefaultRequestRunner$1.run(DefaultRequestRunner.java:217)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:841)
     Caused by: java.lang.InstantiationException: can't instantiate class trees.park.cal.smoke.models.User; no empty constructor
            at java.lang.Class.newInstanceImpl(Native Method)
            at java.lang.Class.newInstance(Class.java:1208)
            at com.google.api.client.util.Types.newInstance(Types.java:116)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:763)
            at com.google.api.client.json.JsonParser.parseArray(JsonParser.java:647)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:739)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:381)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:354)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:87)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81)
            at com.google.api.client.http.HttpResponse.parseAs(HttpResponse.java:459)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:27)
            at trees.park.cal.smoke.server.UserRequest.loadDataFromNetwork(UserRequest.java:12)
            at com.octo.android.robospice.request.CachedSpiceRequest.loadDataFromNetwork(CachedSpiceRequest.java:48)
            at com.octo.android.robospice.request.DefaultRequestRunner.processRequest(DefaultRequestRunner.java:150)
            at com.octo.android.robospice.request.DefaultRequestRunner$1.run(DefaultRequestRunner.java:217)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:841)

However, if I use the an ObjectMapper the deserialization happens flawlessly. An example can be found in my LoginActivity (which I just wrote for testing purposes):

public class LoginActivity extends Activity{

    private SpiceManager spiceManager = new SpiceManager(JacksonGoogleHttpClientSpiceService.class);

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

        String json = "[\n" +
                "    {\n" +
                "        \"id\": 1, \n" +
                "        \"email\": \"[email protected]\", \n" +
                "        \"password\": \"password\"\n" +
                "    }, \n" +
                "    {\n" +
                "        \"id\": 2, \n" +
                "        \"email\": \"[email protected]\", \n" +
                "        \"password\": \"\"\n" +
                "    }, \n" +
                "    {\n" +
                "        \"id\": 3, \n" +
                "        \"email\": \"[email protected]\", \n" +
                "        \"password\": \"\"\n" +
                "    }, \n" +
                "    {\n" +
                "        \"id\": 4, \n" +
                "        \"email\": \"[email protected]\", \n" +
                "        \"password\": \"password\"\n" +
                "    }\n" +
                "]";
        ObjectMapper mapper = new ObjectMapper();
        try {
            UserList result = mapper.readValue(json, UserList.class);
            System.out.println("test");

        } catch (IOException e) {
            e.printStackTrace();
        }

        //stuff
    }
}

If I manually replace the response.parseAs(getRequestType()) with an ObjectMapper and manually get the input stream from the HttpResponse then I can deserialize the response perfectly.

Why can't the JsonObjectParser do what the ObjectMapper does? How can I fix this problem?

Thanks

Upvotes: 0

Views: 894

Answers (1)

Lukas V
Lukas V

Reputation: 423

Answer is in stacktrace

Caused by: java.lang.InstantiationException: can't instantiate class trees.park.cal.smoke.models.User; no empty constructor

Try add contructor without parameters to User class

public User() {
}

Upvotes: 1

Related Questions