-
Notifications
You must be signed in to change notification settings - Fork 16
Serializing Request Content
A request body may carry a payload in a format which is expected by the server. Common standardized formats are defined by Internet Media Types (also known as MIME types or Content-Types). These are recognized by a special identifier written as type/subtype
, for example application/json
, application/xml
. A serializer is responsible for converting a given model (the payload) to an expected format which might be standardized or customized.
####1. Attaching a Serializer
Serializers are attached using an @Serialize
annotation which takes the ContentType
to identify the proper serializer to be used. This annotation can be placed either on an endpoint or a request. If placed on an endpoint, the serializer is used for all requests defined on the endpoint. If a specific request requires an alternate serializer, the root definition can be overridden with another @Serialize
annotation on the request.
import static com.lonepulse.robozombie.annotation.Entity.ContentType.JSON;
import static com.lonepulse.robozombie.annotation.Entity.ContentType.XML;
@Serialize(JSON)
@Endpoint("https://example.com")
public interface ExampleEndpoint {
@PUT("/json")
void putJsonContent(@Entity Content content);
@PUT("/xml")
@Serialize(XML)
void putXmlContent(@Entity Content content);
}
####2. Using Prefabricated Serializers
Out-of-the-box serializers are provided for the content-types text/plain
, application/json
and application/xml
. These can be used for plain text, JSON or XML content respectively.
Note that Gson is required for JSON serialization and Simple-XML is required for XML serialization. If these two libraries are not detected on the build-path, the JSON and XML serializers will be disabled and any usage will result in an informative warning.
Serializers for `application/json` and `application/xml` work by converting a model into a JSON or XML `String`. For JSON, unless a custom [field naming policy](https://sites.google.com/site/gson/gson-user-guide#TOC-JSON-Field-Naming-Support) is used, the variable names of the class will be used in the JSON string. Likewise for XML, if [explicit element and attribute naming](http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php) is not used, the variable names of the class will be used for the XML elements.
public class Issue {
private String title;
private String body;
private String assignee;
private List<String> labels;
...
}
@Serialize(JSON)
@Endpoint("https://api.github.com")
public interface GitHubEndpoint {
@POST("/repos/sahan/RoboZombie/issues")
void postIssue(@Entity Issue issue);
}
...
Issue issue = new Issue();
issue.setTitle("Update Wiki");
issue.setBody("Revise wiki pages.")
issue.setAssignee("sahan");
issue.setLabels(Arrays.asList("documentation", "backlog"));
gitHubEndpoint.postIssue(issue);
request-body: {"title": "Update Wiki", "body": "Revise wiki pages.", "assignee": "sahan", "labels": ["documentation", "backlog"]}
Attaching a serializer for `text\plain` via `@Serialize(PLAIN)` will simply invoke `String.valueOf(...)` on the given model. Use `@Serialize(PLAIN)` on requests when you need to override a serializer attached at the endpoint level.
@Serialize(JSON)
@Endpoint("https://example.com")
public interface ExampleEndpoint {
@PUT("/json")
void putModel(@Entity Content content);
@PUT("/json")
@Serialize(PLAIN)
void putJson(@Entity StringBuilder json);
}
####3. Creating Custom Serializers
If an out-of-the-box serializer is not a good match, you're free to create your own custom serializer by extending AbstractSerializer
and overriding serialize(...)
. This extension should be parameterized to the input and output types and the default constructor should be exposed which calls the super constructor with the Class
of the output type.
Note that all serializers exist as singletons and are expected to be thread-safe. If an incurred state affects thread-safety, ensure that proper thread-local management is performed.
import org.apache.http.util.EntityUtils;
import some.third.party.lib.Heap;
final class HeapSerializer extends AbstractSerializer<Heap, String> {
public HeapSerializer() {
super(String.class);
}
@Override
protected String serialize(InvocationContext context, Heap heap) {
return heap.deflate();
}
}
You might even extend AbstractSerializer
to create your own JSON serializer with a custom Gson
instance.
import some.third.party.lib.Model;
final class CustomJsonSerializer extends AbstractSerializer<Object, String> {
private final Gson gson;
public CustomJsonSerializer() {
super(String.class);
this.gson = new GsonBuilder()
.serializeNulls()
.excludeFieldsWithModifier(Modifier.STATIC)
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.registerTypeAdapter(Model.class, new InstanceCreator<Model>() {
@Override
public Model createInstance(Type type) {
return Model.newInstance(); //default constructor unavailable
}
})
.create();
}
@Override
protected String serialize(InvocationContext context, Object input) {
Type type = TypeToken.get(input.getClass()).getType();
return gson.toJson(input, type);
}
}
Custom serializers are attached by specifying their Class
on the type property, e.g. @Serialize(type = CustomSerializer.class)
.
@Endpoint("https://example.com")
@Serialize(type = CustomJsonSerializer.class)
public interface ExampleEndpoint {
@PUT("/json")
void putJsonContent(@Entity Content content);
@PUT("/heap")
@Serialize(type = HeapSerializer.class)
void putHeap(@Entity Heap heap);
}