Search code examples
javagoogle-app-enginegsonrestlet

Using Gson for Restlet to convert Post data (Representation) to an Object


I am trying to post a form to a Restlet ServerResource and read it into an object using Gson Restlet Extension.

There's no documentation on how to use it and nothing on StackOverflow.

What is the correct way of using gson restlet extension?

Following is what I have tried so far:

public class CustomerSegment {
    private int visitsMin;
    private int visitsMax;
    // Getters, Setters and constructors
}

public class CampaignsResource extends ServerResource {
    @Post
    public Representation createCampaign(Representation entity) {
        Form form = new Form(entity);
        // Using form is the usual way, which works fine
        // form: [[visitsMin=3], [visitsMax=6]]

        CustomerSegment segment = null;
        // Following hasn't worked 
        GsonConverter converter = new GsonConverter();
        try {
            segment = converter.toObject(entity, CustomerSegment.class, this);
            //segment = null
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        GsonRepresentation<CustomerSegment> gson
        = new GsonRepresentation<CustomerSegment>(entity, CustomerSegment.class);
        try {
            segment = gson.getObject();
            //NullPointerException
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new EmptyRepresentation();
    }
} 

Form data that is being posted: Form Post Data


Solution

  • In fact, you can leverage the built-in converter support of Restlet without explicitly use the gson converter.

    In fact, when you put the GSON extension within the classpath, the converter it contains is automatically registered within the Restlet engine itself. To check that you can simply use these lines when starting your application:

    List<ConverterHelper> converters
           = Engine.getInstance().getRegisteredConverters();
    for (ConverterHelper converterHelper : converters) {
        System.out.println("- " + converterHelper);
    }
    
    /* This will print this in your case:
       - org.restlet.ext.gson.GsonConverter@2085ce5a
       - org.restlet.engine.converter.DefaultConverter@30ae8764
       - org.restlet.engine.converter.StatusInfoHtmlConverter@123acf34
    */
    

    Then you can rely on beans within signatures of methods in your server resources instead of class Representation, as described below:

    public class MyServerResource extends ServerResource {
        @Post
        public SomeOutputBean handleBean(SomeInputBean input) {
            (...)
            SomeOutputBean bean = new SomeOutputBean();
            bean.setId(10);
            bean.setName("some name");
            return bean;
        }
    }
    

    This works in both sides:

    • Deserialization of the request content into a bean that is provided as parameter of the handling method in the server resource.
    • Serialization into the response content of the returned bean.

    You don't have anything more to do here.

    For the client side, you can leverage the same mechanism. It's based on the annotated interfaces. For this, you need to create an interface defining what can be called on the resource. For our previous sample, it would be something like that:

    public interface MyResource {
        @Post
        SomeOutputBean handleBean(SomeInputBean input);
    }
    

    Then you can use it with a client resource, as described below:

    String url = "http://localhost:8182/test";
    
    ClientResource cr = new ClientResource(url);
    MyResource resource = cr.wrap(MyResource.class);
    SomeInputBean input = new SomeInputBean();
    SomeOutputBean output = resource.handleBean(input);
    

    So in your case, I would refactor your code as described below:

    public class CampaignsResource extends ServerResource {
        private String getUri() {
            Reference resourceRef = getRequest().getResourceRef();
            return resourceRef.toString();
        }
    
        @Post
        public void createCampaign(CustomerSegment segment) {
            // Handle segment
            (...)
    
            // You can return something if the client expects
            // to have something returned
            // For creation on POST method, returning a 204 status
            // code with a Location header is enough...
            getResponse().setLocationRef(getUri() + addedSegmentId);
        }
    }
    

    You can leverage for example the content type application/json to send data as JSON:

    {
      visitsMin: 2,
      visitsMax: 11
    }
    

    If you want to use Gson, you should use this content type instead of the urlencoded one since the tool targets JSON conversion:

    Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary Java objects including pre-existing objects that you do not have source-code of.

    Hope it helps you, Thierry