users@jersey.java.net

Jersey / JAXB incorrectly consumes JSON if elements are not particularly ordered

From: Alex Treppass <alextreppass_at_googlemail.com>
Date: Tue, 23 Mar 2010 11:18:44 +0000

The web service I am writing using Jersey / JAXB plays perfectly with
@Consuming and @Producing XML from JAXB-annotated POJOs. However when
working with JSON, JAXB (or Jersey?) seems to require a brittle ordering of
JSON elements. I am using Jersey 1.1.5 and JaxB 2.2 ri-20091104 (latest).

I've created a simple Web application project to demonstrate:

POJO
---------------
@XmlRootElement(name = "Test")
@XmlAccessorType(XmlAccessType.FIELD)
public class Test
{
    @XmlAttribute
    private int int1;
    @XmlElement
    private String string1;
    @XmlElement
    private List<String> strings;
    @XmlAttribute
    private int int2;
    @XmlAttribute
    private long long1;

    public Test()
    {
        super();
    }

    public Test(int int1, String string1, List<String> strings, int int2,
long long1)
    {
        super();
        this.int1 = int1;
        this.int2 = int2;
        this.long1 = long1;
        this.string1 = string1;
        this.strings = strings;
    }

    ... hashcode & equals omitted.
}

Interface
---------------
@Path("test")
public interface IView
{
    @GET
    @Path("get/")
    @Produces(MediaType.APPLICATION_JSON)
    public abstract Test getTest();

    @POST
    @Path("post/")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public abstract Test takeTest(final Test test);
}

Implementation
---------------
@Provider
public class View implements IView
{
    public Test getTest()
    {
        return new Test(1, "string", Arrays.asList(new String[] { "s1", "s2"
}), 2, 3L);
    }

    public Test takeTest(final Test test)
    {
        return test;
    }
}

When I request /test/get, JAXB returns me the following JSON every time,
even though I have no @XmlType(propOrder =) class annotation set. This is
fine - I don't mind an arbitrary ordering for output.
(Note that I am using a custom ContextResolver<JAXBContext> to read &
return JSONConfiguration.natural() notation for my Test class)
    {"int1":1,"int2":2,"long1":3,"string1":"string","strings":["s1","s2"]}

However, when I try and consume JSON that does not match this order, strange
things happen and my int / long fields are not set. This is shown in the
following example:

Post:
 {"int1":1,"string1":"string","strings":["s1","s2"],"int2":2,"long1":3}
Response:
{"int1":1,"int2":0,"long1":0,"string1":"string","strings":["s1","s2"]}
equals(): false
[...fields int2 and long1 are not being set.]
----------

I've created a test script which constructs various JSON strings and posts
them off to my takeTest() method, which returns a JSON representation of the
parsed Test object.
I then use Jackson within my test script to build this into a new Test
object (via a JaxbAnnotationIntrospector) and test equality on the two
objects.

Post:
{"string1":"string","strings":["s1","s2"],"int1":1,"int2":2,"long1":3}
Response:
{"int1":0,"int2":0,"long1":0,"string1":"string","strings":["s1","s2"]}
equals(): false
[... none of the int or long fields are being set]
----------

Post:
{"int1":1,"int2":2,"string1":"string","strings":["s1","s2"],"long1":3}
Response:
{"int1":1,"int2":2,"long1":0,"string1":"string","strings":["s1","s2"]}
equals(): false
[... int1 and int2 are correctly set, but long1 is not set]
----------

Post:
 {"int1":1,"int2":2,"long1":3,"string1":"string","strings":["s1","s2"]}
Response:
{"int1":1,"int2":2,"long1":3,"string1":"string","strings":["s1","s2"]}
equals(): true
----------

If I attach an @XmlType(propOrder = { "int1", "int2", "long1", "string1",
"strings" }) annotation to my Test class, Jackson will happily follow this
and generate JSON that matches JAXB's expected ordering. I don't however
think the onus should be on clients to guarantee the 'primitives first'
ordering that Jersey / JAXB seems to expect.

My issue here is that if I'm using a particular JSON library (in whatever
language, divorced from my server) to post JSON at /test/post/ I cannot
guarantee element ordering. Is this a bug with Jersey's use of JAXB? Or
potentially JAXB by itself?