Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind complex (JSON) form data automatically

My JSON data coming in request().body().asFormUrlEncoded().get("records")

[{"string":"foo","termId":"793340"},{"string":"bar","termId":"460288"}]

My form definition:

public static class MyForm {
    @Constraints.Required
    public List<Map<String,String>> records;
    public String someField;
}

It doesn't bind the records automatically. Then I tried with a POJO instead:

public static class Record {
    public String string;
    public String termId;
    public void setString(String string) {
        this.string = string;
    }
    public void setTermId(String termId) {
        this.termId = termId;
    }
}

And adapted the form:

public static class MyForm {
    @Constraints.Required
    public List<Record> records;
    public String someField;
}

It doesn't bind the data automatically either. Do I really need to use low level APIs like jackson for this simple use case? Any pointer? Couldn't find a copy/paste example, and from jackson I have org.codehaus.jackson and com.fasterxml.jackson on my classpath.

UPDATE 2013-05-10: added a secondary field someField to clarify that the records is just one field, not the whole data structure. The answer below from (I cannot see the answers on this edit screens, so never mind, there is just one) works, but only with the records. Here's an example:

private List<Record> recordsFromRequest() {
    String[] jsonData = request().body().asFormUrlEncoded().get("records");
    Form<Record> recordDummyForm = Form.form(Record.class);
    Iterator<JsonNode> it = Json.parse(jsonData[0]).iterator();
    List<Record> records = new ArrayList<>();
    while (it.hasNext()) {
        records.add(recordDummyForm.bind(it.next()).get());
    }
    return records;
}

For the other form fields I do, as usual:

Form<MyForm> form = play.data.Form.form(MyForm.class).bindFromRequest();

So right now I get to all the posted form data, and my problem is solved this way (thanks!). However, it's a bit ugly. What I can't figure out yet is how to have all post data in one object. If someone replies to this then I'll update the question and remove this part. Otherwise I'll accept the single answer in a couple of days.

like image 558
Gonfi den Tschal Avatar asked May 09 '13 06:05

Gonfi den Tschal


2 Answers

In my opinion, you should use jackson API's as described in the official documentation here.

I assume that you get the JSON with request().body().asFormUrlEncoded().get(), so it return String[] containing your JSON String. You can do something like this (Maybe a little complicated one and miss Exception handling) :

String[] jsonData = request().body().asFormUrlEncoded().get("records")
MyForm myForm = new MyForm();
// Record should act as form, because each JSON string data contain this type
Form<Record> form = Form.form(Record.class);
// parse the JSON string and assign iterator 
Iterator<JsonNode> it = Json.parse(jsonData[0]).iterator(); 
// assign to the MyForm instance
while (it.hasNext()) {
    formData.records.add(form.bind(it.next()).get()); // bind the JSON and add
}

So, at the end of the code above, ((MyForm) formData).records should contains List<Record> object from your JSON.

like image 159
Wayan Wiprayoga Avatar answered Nov 15 '22 00:11

Wayan Wiprayoga


I was trying to reproduce your error. So I created a model as follows:

public class EasyContact {

public String someField;

@Required
public List<Record> records;

public static class Record {
    public String string;
    public String termId;
    @Override
    public String toString() {
        return "Record [string=" + string + ", termId=" + termId + "]";
    }
}

@Override
public String toString() {
    return "EasyContact [someField=" + someField + ", records=" + records+ "]";
}
}

Then A simple controller like this:

    public static Result submit() {
    Form<EasyContact> filledForm = form(EasyContact.class).bindFromRequest();
    Logger.info("Posted binding: " + filledForm.get().toString());
    return ok();
}

Now is time to test:

curl -X POST -H 'Content-Type: application/json' -d '{"someField":"didac", "records": [{"string": "string1", "termId": "111"},{"string": "string2", "termId": "222"}]}' localhost:9000/contacts

In the play command line I can see the correct output:

[info] application - Posted binding: EasyContact [someField=didac, records=[Record [string=string1, termId=111], Record [string=string2, termId=222]]]

The only error I found is when the Content-Type is not set in the request (-H ...). In that case, the framework Throws an IllegalState exception.

like image 37
Didac Montero Avatar answered Nov 15 '22 01:11

Didac Montero