Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jersey REST Client - Treat Custom MediaType as MediaType.APPLICATION_JSON

I'm writing a REST client using Jersey with JacksonFeature enabled for a webservice that forces me to specify their custom-named content type, even though it's just regular JSON. In other words, when I do this:

Request request = buildMySampleRequestPojo();

Response response = requestBuilder.post(
        Entity.entity(request, MediaType.APPLICATION_JSON)
);

The service complains that I'm using an invalid content type. I can get around this by specifying their custom-named media type in place of the MediaType.APPLICATION_JSON constant:

Response response = requestBuilder.post(
        Entity.entity(request, "vnd.stupidNameThatReallyIsJustJSON+json")
);

However, when I do that, I get:

*SEVERE: MessageBodyWriter not found for media type=stupidNameThatReallyIsJustJSON*

Is there a way I can have Jersey treat this customized media type name as if it were regular JSON without writing a customized MessageBodyWriter?

like image 219
kwikness Avatar asked Jun 02 '15 19:06

kwikness


People also ask

What is MediaType Application_json?

APPLICATION_JSON is a "public constant media type for application/json ", whereas MediaType. APPLICATION_JSON_VALUE is a "String equivalent of MediaType. APPLICATION_JSON ". Attributes on Java annotations can only be one of a limited set of types. This prevents MediaType from being used as an annotation attribute.

What is the difference between Jax-RS and Jersey?

JAX-RS is a specification (which basically tells what to implement/follow) and Jersey is an implementation (which means how those specifications should be implemented). We can have multiple implementations for a Specification.

What is difference between Jersey and spring rest?

Jersey is the JAX-RS API example implementation provided by Sun, while Spring REST is of course Spring's implementation of the same API/JSRs. The major difference is that Spring REST easily integrates into other Spring APIs (if you wish) such as Spring Data Rest.


1 Answers

I think you could use both this (JAX-RS entity providers) and this (use Jackson with Jersey), in order to achieve what you want. In a nutshell, register a MessageBodyWriter annotated with @Produces("application/vnd.stupidNameThatReallyIsJustJSON+json") and in the implementation just delegate the marshalling/unmarshalling to Jackson.

EDIT : try with something along the lines of

package my.pack.age;

import com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider;
import com.sun.jersey.core.util.ReaderWriter;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

@Produces("application/vnd.stupidNameThatReallyIsJustJSON+json")
public class MyJsonBodyWriter<T> extends AbstractMessageReaderWriterProvider<T> {

    // T should be your pojo in this case. If you can make your pojo compatible with org.codehaus.jettison.json.JSONObject,
    // then you can extend com.sun.jersey.json.impl.provider.entity.JSONObjectProvider and delegate all the methods of
    // MessageBodyWriter (and MessageBodyReader) to that. Otherwise just implement them.

    @Override
    public T readFrom(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType,MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
        try {
//            deserialize entityStream according to the given media type and return a new instance of T.
//            
//            You can do it like this (just an example) :
//            JSONObject myObject = new JSONObject();
//            try {
//                these names and values should come from the stream.
//                myObject.put("name", "Agamemnon");
//                myObject.put("age", 32);
//            } catch (JSONException ex) {
//                LOGGER.log(Level.SEVERE, "Error ...", ex);
//            }
            return null;
        } catch (Exception e) {
            throw new WebApplicationException(new Exception("Error during deserialization", e),400);
        }
    }

        @Override
    public void writeTo(T t,Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
        try {
            OutputStreamWriter writer = new OutputStreamWriter(entityStream, ReaderWriter.getCharset(mediaType));
            // write t on the writer
            writer.flush();
        } catch (Exception e) {
            throw new WebApplicationException( new Exception("Error during serialization", e), 500);
        }
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        // should return true if this class can serialize the given type to the given media type
        return true;
    }

    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        // should return true if this class can deserialize the given type to the given media type
        return true;
    }

    @Override
    public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        // calculate and return content-lenght, i.e. the lenght of the serialized representation of t
        return 0;
    }

}

Obviously this is just a starting point, not a working example, but it should give you enough information to start. Also remember that you'll have to register the class to Jersey in order to let it use it.

like image 107
francesco foresti Avatar answered Sep 21 '22 05:09

francesco foresti