[JAX-RS] Implementing custom MessageBodyWriter for serialization

Say you want to serialize your object in a custom format. For XML you can use JAXB, for JSON you can use Jackson. But in case you have your own specific format, you need to implement your own MessageBodyWriter.

1. Maven web application – pom.xml (here jersey 1.19, feel free to upgrade):

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.vvirlan</groupId>
	<artifactId>streamingoutput</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>streamingoutput Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-server</artifactId>
			<version>1.19</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-grizzly2</artifactId>
			<version>1.19</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>streamingoutput</finalName>
	</build>
</project>

2. Application class. Note that besides the actual resource class, you need to also add your custom writer.

package com.vvirlan;

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import com.vvirlan.resources.CustomerResource;
import com.vvirlan.resources.MyCustomWriter;

@ApplicationPath("rest")
public class Application extends javax.ws.rs.core.Application{
	@Override
	public Set<Class<?>> getClasses() {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		classes.add(CustomerResource.class);
		classes.add(MyCustomWriter.class);
		return classes;
	}
}

3. The resource class (CustomerResource):

package com.vvirlan.resources;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("customers")
@Produces(MediaType.TEXT_PLAIN)
public class CustomerResource {
	@GET
	@Path("c")
	@Pretty
	public Customer getCustomer() {
		return new Customer("A",2,2);
	}
}

4. The Customer object:

package com.vvirlan.resources;

//My custom annotation (makes it eligible for my serialization)
@MyContent
public class Customer {

	private String name;
	private int age;
	private int size;

//Mandatory
	public Customer() {

	}

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

	// Getters and setters
}

5. My MyContent annotation

package com.vvirlan.resources;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyContent {

}

6. The actual custom body writer:

package com.vvirlan.resources;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;

@Provider
@Produces(MediaType.TEXT_PLAIN)
public class MyCustomWriter implements MessageBodyWriter<Customer> {

	public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
		boolean present = type.isAnnotationPresent(MyContent.class);
		return present;
	}

	public long getSize(Customer t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
		// Leave it to Jersey to put content-length
		return -1;
	}

	public void writeTo(Customer t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
			MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
					throws IOException, WebApplicationException {

		// Here reflection may be used to get the custom serialization
		StringBuilder sb = new StringBuilder();
		sb.append("Name: " + t.getName() + " Age: " + t.getAge() + " Size: " + t.getSize());

		entityStream.write(sb.toString().getBytes());

	}
}

7. Accessing the application at:
http://localhost:8080/yourprojectname/rest/customers/c/

And you will see:
Name: A Age: 2 Size: 2

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s