Mark
Mark

Reputation: 2227

GluonConnect JSON converter cannot convert object

On the client app I have this POJO

public class Chicken {

    private String name;
    private int age;

    public Chicken(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

and I run this

RestClient get = RestClient.create().method("GET")
        .host("http://localhost:8080/DevCrowd")
        .path("resources/chickens");
GluonObservableList<Chicken> sample = DataProvider.retrieveList(
        get.createListDataReader(Chicken.class));
System.out.println(sample);

But I get the error:

WARNING: Failed to create object of type class com.devcrowd.test.Chicken from the following json object {"id":1,"name":"AAA","age":12}
java.lang.InstantiationException: com.gluonhq.notesapp.Chicken
    at java.lang.Class.newInstance(Class.java:427)
    at com.gluonhq.connect.converter.JsonConverter.readFromJson(JsonConverter.java:111)
    at com.gluonhq.connect.converter.JsonIterableInputConverter.next(JsonIterableInputConverter.java:108)
    at com.gluonhq.connect.provider.DataProvider.lambda$retrieveList$21(DataProvider.java:194)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoSuchMethodException: com.gluonhq.notesapp.Chicken.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 6 more

On the server I have this entity:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
@Entity
@NamedQuery(name="all", query = "SELECT c FROM Chicken C")
public class Chicken {

    @Id
    @GeneratedValue
    private long id;
    private String name;
    private int age;

    public Chicken() {}

    public Chicken(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

and this service:

@Path("chickens")
public class ChickensResource {

    @Inject
    ChickenService cs;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String chickens() {
        JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
        List<Chicken> chickens = cs.getAllChickens();
        chickens.stream().map(chicken -> Json.createObjectBuilder()
            .add("name", chicken.getName())
            .add("age", chicken.getAge())
            .build())
            .forEach(jsonArrayBuilder::add);
        return jsonArrayBuilder.build().toString();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public void save(JsonObject chicken) {
        String name = chicken.getString("name");
        int age = chicken.getInt("age");
        cs.save(new Chicken(name, age));
    }
}

I can POST correctly (I check the DB and what I POST is there so this is why the error stack has a Chicken object ready) but I can't read it back. Why is that?

Upvotes: 1

Views: 358

Answers (1)

Jos&#233; Pereda
Jos&#233; Pereda

Reputation: 45476

As you can read in the docs for JsonConverter::readFromJson:

Convert the provided JSON Object into a Java object. If a new instance could not be created from the specified targetClass in the constructor, then null will be returned.

The conversion works by inspecting all the property methods of the target class. A property method is any field that has both a getter and a setter method.

Now if you check your exception:

java.lang.InstantiationException: com.gluonhq.notesapp.Chicken
at java.lang.Class.newInstance(Class.java:427)

the reason becomes clear: the target class (com.gluonhq.notesapp.Chicken) can't be instantiated, because it looks for a parameterless constructor.

All you'll need to do is add one:

public class Chicken {

    private String name;
    private int age;

    public Chicken() { }

    public Chicken(String name, int age) {
        this.name = name;
        this.age = age;
    } 
    ...
}

EDIT

The DataProvider returns an observable list, and you should use the initializedProperty() to find out when the list is ready, so you can get its content:

RestClient get = RestClient.create().method("GET")
            .host("http://localhost:8080/DevCrowd")
            .path("/resources/chickens");

GluonObservableList<Chicken> sample =  DataProvider.retrieveList(
           get.createListDataReader(Chicken.class));

sample.initializedProperty().addListener((obs, ov, nv) -> {
    if (nv) {
        for (Chicken chicken : sample) {
            System.out.println(chicken);
        }
    }
});

Upvotes: 1

Related Questions