Search code examples
javajsonrestjakarta-eejersey

How do I use the Jersey JSON POJO support?


I have an object that I'd like to serve in JSON as a RESTful resource. I have Jersey's JSON POJO support turned on like so (in web.xml):

<servlet>  
    <servlet-name>Jersey Web Application</servlet-name>  
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>  
</servlet>  

But when I try to access the resource, I get this exception:

SEVERE: A message body writer for Java type, class com.example.MyDto, and MIME media type, application/json, was not found
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException
...

The class that I'm trying to serve isn't complicated, all it's got are some public final fields and a constructor that sets all of them. The fields are all strings, primitives, classes similar to this one, or Lists thereof (I've tried using plain Lists instead of generic List<T>s, to no avail). Does anyone know what gives? Thanks!

Java EE 6

Jersey 1.1.5

GlassFish 3.0.1


Solution

  • Jersey-json has a JAXB implementation. The reason you're getting that exception is because you don't have a Provider registered, or more specifically a MessageBodyWriter. You need to register a proper context within your provider:

    @Provider
    public class JAXBContextResolver implements ContextResolver<JAXBContext> {
        private final static String ENTITY_PACKAGE = "package.goes.here";
        private final static JAXBContext context;
        static {
            try {
                context = new JAXBContextAdapter(new JSONJAXBContext(JSONConfiguration.mapped().rootUnwrapping(false).build(), ENTITY_PACKAGE));
            } catch (final JAXBException ex) {
                throw new IllegalStateException("Could not resolve JAXBContext.", ex);
            }
        }
    
        public JAXBContext getContext(final Class<?> type) {
            try {
                if (type.getPackage().getName().contains(ENTITY_PACKAGE)) {
                    return context;
                }
            } catch (final Exception ex) {
                // trap, just return null
            }
            return null;
        }
    
        public static final class JAXBContextAdapter extends JAXBContext {
            private final JAXBContext context;
    
            public JAXBContextAdapter(final JAXBContext context) {
                this.context = context;
            }
    
            @Override
            public Marshaller createMarshaller() {
                Marshaller marshaller = null;
                try {
                    marshaller = context.createMarshaller();
                    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                } catch (final PropertyException pe) {
                    return marshaller;
                } catch (final JAXBException jbe) {
                    return null;
                }
                return marshaller;
            }
    
            @Override
            public Unmarshaller createUnmarshaller() throws JAXBException {
                final Unmarshaller unmarshaller = context.createUnmarshaller();
                unmarshaller.setEventHandler(new DefaultValidationEventHandler());
                return unmarshaller;
            }
    
            @Override
            public Validator createValidator() throws JAXBException {
                return context.createValidator();
            }
        }
    }
    

    This looks up for an @XmlRegistry within the provided package name, which is a package that contains @XmlRootElement annotated POJOs.

    @XmlRootElement
    public class Person {
    
        private String firstName;
    
        //getters and setters, etc.
    }
    

    then create an ObjectFactory in the same package:

    @XmlRegistry
    public class ObjectFactory {
       public Person createNewPerson() {
          return new Person();
       }
    }
    

    With the @Provider registered, Jersey should facilitate the marshalling for you in your resource:

    @GET
    @Consumes(MediaType.APPLICATION_JSON)
    public Response doWork(Person person) {
       // do work
       return Response.ok().build();
    }