Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB or JAX-RS is wrapping numbers in my JSON responses with quotes, turning them into strings. Why this is the default behavior, and how to fix it?

Tags:

java

jaxb

jax-rs

I am currently working on a RESTful API. I have an Employee class and an EmployeeResource class. I also have a custom DateAdapter, which changes my Date properties to Long timestamps. However, my JSON responses are showing the timestamps as strings (wrapped in double quotes) rather than numbers (without double quotes). Here is an abbreviated version of my code and captured JSON response...

Custom DateAdapter

public class DateAdapter extends XmlAdapter<Long, Date> {
    @Override
    public Date unmarshal(Long v) throws Exception {
        return new Date(Long.valueOf(v));  
    }
    @Override
    public Long marshal(Date v) throws Exception {
        return v.getTime();  
    }
}

Entity Class

@Entity
@javax.xml.bind.annotation.XmlRootElement
@XmlType(propOrder={"createdOn","empId"})
public class Employee implements Serializable {
    private Date createdOn;
    private Integer empId;

    @Column(nullable=false)
    @Temporal(TemporalType.TIMESTAMP)
    @XmlJavaTypeAdapter(DateAdapter.class)
    public Date getCreatedOn() {
        return createdOn;
    }
    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    @Id
    @XmlID
    public Integer getEmpId() {
        return empId;
    }
    public void setEmpId(Integer empId) {
        this.empId = empId;
    }
}

EmployeeResource

@Path("/Employees")
@javax.xml.bind.annotation.XmlRootElement 
@XmlType(propOrder={"hateoas","employees"})
public class EmployeeResource {
    List<Employee> employees;

    public List<Employee> getEmployees() {
        return employees;
    }
    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }
    @GET
    @Path("/{id}")
    @Produces("application/json")
    public Response getEmployee(@Context UriInfo ui, @PathParam("id") Integer id) {
        Session session = HibernateUtil.getSession();
        session.beginTransaction();
        Criteria criteria=session.createCriteria(Employee.class);
        criteria.add(Restrictions.eq("empId", new Integer(10150)));
        this.employees = criteria.list();
        return Response.ok(this).build();
    }
}

Current JSON response

{
  "employees":{
    "createdOn":"1330915130163",
    "empId":"10150"
  }
}

Expected JSON response

{
  "employees":{
    "createdOn":1330915130163,
    "empId":10150
  }
}

I'm assuming that there's some way to prevent JAXB or JAX-RS from wrapping all numbers in quotes. Could someone guide me to where I could configure this?

Thanks in advance!

EDIT #1 2012.03.07 Ok, so after some more researching, I think my problem is with the default JSONConfiguration.Notation being used, MAPPED . It looks like the NATURAL JSONConfiguration.Notation would get me what I want. However, I haven't found a clear example of how to apply that application wide. I'm assuming I would specify this in my ApplicationConfig class that extends javax.ws.rs.core.Application .

EDIT #2 2012.03.10 Ok, so after some more researching I decided to use the JSON parser library, Jackson. It seems to be the most complete JSON solution out there using just the default configuration. By default Dates are translated to their corresponding timestamps and serialized as numbers (w/o quotes). The only drawback I have come across is that Jackson currently does not support the JAXB annotations, "@XmlID" and "@XmlIDREF". Since I have direct self-references in my data model (not shown above), I have created another question to discuss. If anyone is interested click here to follow that thread...

like image 424
hypno7oad Avatar asked Mar 07 '12 03:03

hypno7oad


People also ask

What is the use of JAXB in Web service?

Java Architecture for XML Binding (JAXB) is an XML-to-Java binding technology that simplifies the development of web services by enabling transformations between schema and Java objects and between XML instance documents and Java object instances.

Which API is used for generating XML as a response in Jax-RS?

Your JAX-RS application can use the JAXB objects to manipulate XML data.


1 Answers

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.

The JAX-RS implementation you are using may be using a JAXB (JSR-222) implementation with something like Jettison to produce JSON. Jettison provides a StAX API to interact with JSON, since that StAX APIs don't have any sort of typing WRT text, all the simple values get treated as strings:

  • http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html

To get the behaviour you are looking for you can use a different binding solution. We are adding this support into the MOXy component for EclipseLink 2.4:

  • http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html

To configure MOXy as your JSON-binding provider in a JAX-RS environment, you could create a MessageBodyReader/MessageBodyWriter that looks like:

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
import javax.xml.transform.stream.StreamSource;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MOXyJSONProvider implements 
    MessageBodyReader<Object>, MessageBodyWriter<Object>{

    @Context
    protected Providers providers;

    public boolean isReadable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public Object readFrom(Class<Object> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
        throws IOException, WebApplicationException {
        try {
            Unmarshaller u = getJAXBContext(type, mediaType).createUnmarshaller();
            u.setProperty("eclipselink.media-type", mediaType.toString());
            u.setProperty("eclipselink.json.include-root", false);//tiny fix
            return u.unmarshal(new StreamSource(entityStream), (Class) genericType);
        } catch(JAXBException jaxbException) {
            throw new WebApplicationException(jaxbException);
        }
    }

    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public void writeTo(Object object, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders,
        OutputStream entityStream) throws IOException,
        WebApplicationException {
        try {
            Marshaller m = getJAXBContext(Customer.class, mediaType).createMarshaller();
            m.setProperty("eclipselink.media-type", mediaType.toString());
            m.setProperty("eclipselink.json.include-root", false);
            m.marshal(object, entityStream);
        } catch(JAXBException jaxbException) {
            throw new WebApplicationException(jaxbException);
        }
    }

    public long getSize(Object t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    private JAXBContext getJAXBContext(Class<?> type, MediaType mediaType) 
        throws JAXBException {
        ContextResolver<JAXBContext> resolver 
            = providers.getContextResolver(JAXBContext.class, mediaType);
        JAXBContext jaxbContext;
        if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
            return JAXBContext.newInstance(type);
        } else {
            return jaxbContext;
        }
    }

}

For More Information

  • https://stackoverflow.com/a/8989659/383861
  • http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-15.html
  • http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html
like image 169
bdoughan Avatar answered Oct 06 '22 05:10

bdoughan