users@jersey.java.net

Re: [Jersey] Jersey / JAXB incorrectly consumes JSON if elements are not particularly ordered

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Tue, 23 Mar 2010 15:14:13 +0100

On Mar 23, 2010, at 1:51 PM, Jakub Podlesak wrote:

>
> Hi Alex,
>
> could you please zip the web application and send it to me?
> If it is too big, please send it privately (to jakub.podlesak at
> sun.com).
>

I think what is going on here is the Jersey JSON JAXB serialization
requires that what are defined to be XML attributes in the XML
serialization occur first in the JSON serialization. Namely when JAXB
marshalls out stuff it will marshall out the set of attributes first.
When JAXB unmarshalls it expects the set of attributes first.

My guess is you can change the order of the information marked as XML
attributes but not intermix these with the information marked as XML
elements.

Paul.


> Thanks,
>
> ~Jakub
>
> On Tue, Mar 23, 2010 at 11:18:44AM +0000, Alex Treppass wrote:
>> 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?
>
> --
> http://blogs.sun.com/japod
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>