kaushik
kaushik

Reputation: 2492

JAXB using Jersey and Moxy

I am using Tomcat 7 with Jersey and Moxy. I have added jersey.server.jar, jersey.servlet.jar, jersey-core.jar, moxy (org.eclipse.persistence.moxy, version 2.5.0) using maven.

I have Interfaces (package name : org.kkm.test.model):

public interface Model extends BaseModel {
    public String getConsumerId();
    public void setConsumerId(String consumerId);
}

@XmlRootElement(name="reading")
@XmlType(propOrder={"consumerId", "address", "reading", "readingDate", "rotation", "readingStatus"})
public interface Reading extends Model {
    @XmlElement
    public Address getAddress();
    @XmlElement
    public double getReading();
    @XmlElement
    public Date getReadingDate();
    @XmlElement
    public short getRotation();
    @XmlElement
    public ReadingStatus getReadingStatus();

    public void setAddress(Address address);
    public void setReading(double reading);
    public void setReadingDate(Date readingDate);
    public void setRotation(short rotation);
    public void setReadingStatus(ReadingStatus readingStatus);
}


public interface Address {
    public String getAddress1();
    public String getAddress2();
    public String getAddress3();
    public String getAddress4();

    public void setAddress1(String address1);
    public void setAddress2(String address2);
    public void setAddress3(String address3);
    public void setAddress4(String address4);
}

I have implementations ReadingImpl and AddressImpl (package name : org.kkm.test.model.impl). I have a service class as follows:

@Path("/sample")
public class TestService {
    @GET
    @Produces(MediaType.APPLICATION_XML)
    @Path("/readings")
    public List<Reading> getReadings() {
        Reading reading1 = DependencySupplier.getReading();
        Address address = DependencySupplier.getAddress(); 
        address.setAddress1("Moolekkadavil");
        address.setAddress2("Kottuvallikad");
        address.setAddress3("Moothakunnam.P.O");
        address.setAddress4("683516");
        reading1.setAddress(address);
        reading1.setConsumerId("1234567890");
        reading1.setReading(11.00);
        reading1.setReadingDate(new Date());
        reading1.setReadingStatus(ReadingStatus.BILLED);
        reading1.setRotation((short) 1);
        Reading reading2 = DependencySupplier.getReading();
        reading2.setAddress(address);
        reading2.setConsumerId("1234567890");
        reading2.setReading(11.00);
        reading2.setReadingDate(new Date());
        reading2.setReadingStatus(ReadingStatus.BILLED);
        reading2.setRotation((short) 1);
        List<Reading> list = new ArrayList<Reading>();
        list.add(reading1);
        list.add(reading2);
        return list;
    }
    @GET
    @Produces(MediaType.APPLICATION_XML)
    @Path("/reading")
    public Reading getReading() {
        Reading reading1 = DependencySupplier.getReading();
        Address address = DependencySupplier.getAddress(); 
        address.setAddress1("Moolekkadavil");
        address.setAddress2("Kottuvallikad");
        address.setAddress3("Moothakunnam.P.O");
        address.setAddress4("683516");
        reading1.setAddress(address);
        reading1.setConsumerId("1234567890");
        reading1.setReading(11.00);
        reading1.setReadingDate(new Date());
        reading1.setReadingStatus(ReadingStatus.BILLED);
        reading1.setRotation((short) 1);
        return reading1;
    }
}

I have placed a jaxb.properties file in org.kkm.test.model with the following content so that moxy can be used:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

My web.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <display-name>Test JAX</display-name>
  <servlet>
    <servlet-name>testjax</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>org.kwa.test.service</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>testjax</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app> 

The problem I am facing is: When I am invoking "/rest/sample/readings" I am getting the correct representation in XML, but when I am invoking "/rest/sample/reading" I was getting

com.sun.jersey.api.MessageException: A message body writer for Java class...etc error.

Then I wrote a MessageBodyWriter as follows:

public class MyMessageBodyWriter implements MessageBodyWriter {

    private List<Class<?>> types;

    public MyMessageBodyWriter() {
        types = new ArrayList<Class<?>>();
        types.add(ReadingImpl.class);
    }

    public long getSize(Object obj, Class type, Type genericType, Annotation[] annotations,
            MediaType mediaType) {
        // TODO Auto-generated method stub
        return -1;
    }

    public boolean isWriteable(Class type, Type genericType, Annotation[] annotations,
            MediaType mediaType) {
        return this.types.contains(type);
    }

    public void writeTo(Object target, Class type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap headers, OutputStream outputStream)
            throws IOException, WebApplicationException {
        try {
            JAXBContext ctx = JAXBContext.newInstance(type);
            Marshaller marshaller = ctx.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(target, outputStream);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }       
    }
}

Now when I invoke "/rest/sample/reading" I am getting the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<address>
   <address1>Moolekkadavil</address1>
   <address2>Kottuvallikad</address2>
   <address3>Moothakunnam.P.O</address3>
   <address4>683516</address4>
</address><consumerId>1234567890</consumerId><reading>11.0</reading><readingDate>2013-07-04T21:16:39.128</readingDate><readingStatus>BILLED</readingStatus><rotation>1</rotation>

As you can see there is no root element. So it is taking "address" as root element and content after the "address" element is considered as "Extra content" and shown as error.

Upvotes: 1

Views: 1594

Answers (1)

kaushik
kaushik

Reputation: 2492

Finally, I have found an answer and that was so simple. How couldn't I think of that earlier ? I am not sure whether this will affect performance. You don't need any MessageBodyWriter or anything. Just copy (not move) jaxb.properties file from org.kkm.test.model and place it in the implementation package (i.e, in org.kkm.test.model.impl) and add the @XmlRootElement(name="reading") in the implementation also (i.e, in ReadingImpl). If you want ordering add @XmlType(propOrder....

Upvotes: 1

Related Questions