users@jersey.java.net

RE: [Jersey] Custom Natural JAXBContextResolver Problem - Single Element Arrays

From: James Allchin <james_at_knowledgemill.com>
Date: Fri, 9 Oct 2009 07:54:46 -0700

Thanks Jakub - for your assistance - ultimately it enabled me to solve the problem.

I got the test case you sent me working just fine. I then tried deploying against my tomcat instance which also worked fine.

This was a good start :-)

I then modified my own netbeans project to include your test files - running both as a grizzly server and on tomcat.

In the end it came down to environmental issues with netbeans (and library dependencies) - which aren't fully resolved but the good news it is now in a working state.

Thanks for your assistance - and apologies that it was a bit of red herring. The test case really did help though.

Cheers

James
________________________________________
From: Jakub.Podlesak_at_Sun.COM [Jakub.Podlesak_at_Sun.COM]
Sent: 08 October 2009 16:25
To: users_at_jersey.dev.java.net
Cc: James Allchin
Subject: Re: [Jersey] Custom Natural JAXBContextResolver Problem - Single Element Arrays

Hi James,

one more thing, please see the comments inline....

On Mon, Oct 05, 2009 at 12:25:55PM -0700, James Allchin wrote:
> Hi All,
>
> I have a ContextResolver so that I can use Natural JSON notation:
>
> package com.knowledgemill.rest.main;
>
> import com.knowledgemill.entities.*;
>
> import com.sun.jersey.api.json.JSONJAXBContext;
> import com.sun.jersey.api.json.JSONConfiguration;
> import java.util.HashMap;
> import java.util.HashSet;
> import java.util.Map;
> import javax.ws.rs.ext.ContextResolver;
> import javax.ws.rs.ext.Provider;
> import javax.xml.bind.JAXBContext;
>
> /*
> * This class is used to make sure that when using JSON when an array contains a single
> * element it still comes back as an array - makes it much easier to deal with
> */
>
>
> @Provider
> public class JAXBContextResolver implements ContextResolver<JAXBContext> {
>
> private JAXBContext context;
>
> private Class<?>[] types = {
> Artifact.class
> , ArtifactPermission.class
> , AttachedDoc.class
> , AttachedDocArtifact.class
> , AuditMessage.class
> , Container.class
> , ContainerPermission.class
> , Context.class
> , ContextFolder.class
> , Credential.class
> , DBLogMessage.class
> , EmailArtifact.class
> , EmailRecipient.class
> , ExtendedAttribute.class
> , ExtendedEmail.class
> , Group.class
> , InternalArtifact.class
> , InternalUser.class
> , ManagedDocArtifact.class
> , StorageTier.class
> , SystemAttribute.class
> , TreeNode.class
> , User.class
> , UserEmailAddress.class
> };
>
>
> public JAXBContextResolver() throws Exception {
> Map props = new HashMap<String, Object>();
> this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
> }
>
> public JAXBContext getContext(Class<?> objectType) {
> for (Class<?> type : types) {
> if (type == objectType) {
> return context;
> }
> }
> return null;
> }
>
>
> }
>
>
> As you can see I have a number of classes which I have specified should be targeted by the this ContextResolver.
>
> Now according to the documentation, the NATURAL configuration should mean that single element arrays will indeed be respected as a array within JSON (instead of returning a single object).
>
> If I take one of my methods which returns List<UserEmailAddress>:
>
> @GET
> @Path("user/{userId}/users/{targetUserId}/emailaddresses/")
> @Produces({"application/xml","application/json"})
> public List<UserEmailAddress> getUserEmailAddresses (@PathParam("userId") String userId
> , @PathParam("targetUserId") String targetUserId
> ) {
>
> List<UserEmailAddress> returnedEmailAddresses = null;
> try {
> logger.debug("userId value is " + userId);
> returnedEmailAddresses = dA.getUserEmailAddresses( targetUserId);
> }
> catch (Exception e) {
> e.printStackTrace();
> }
> return returnedEmailAddresses;
> }
>
> If I exercise this call (using JSON) where it returns multiple elements - I end up with a response shown below:
>
> {"UserEmailAddress": [
> {
> "userId": "7452272EBA160D9AE040A8C048010EEB",
> "smtpAddress": "david.campbell_at_knowledgemill.com",
> "primary": "false",
> "legacy": "true"
> },
> {
> "userId": "7452272EBA160D9AE040A8C048010EEB",
> "smtpAddress": "dcampbell_at_knowledgemill.com",
> "primary": "true",
> "legacy": "false"
> }
> ]}

This is bad, as it proves the natural notation is not being used in your case,
otherwise, you would end up with just (no wrapping object present):

[
       {
       "userId": "7452272EBA160D9AE040A8C048010EEB",
       "smtpAddress": "david.campbell_at_knowledgemill.com",
       "primary": "false",
       "legacy": "true"
    },
       {
       "userId": "7452272EBA160D9AE040A8C048010EEB",
       "smtpAddress": "dcampbell_at_knowledgemill.com",
       "primary": "true",
       "legacy": "false"
    }
 ]

>
> This looks good - I can see the array notation in the JSON.
>
> If I exercise this call (using JSON) where it returns a single element - I end up with a response shown below:
>
> {"UserEmailAddress": {
> "userId": "7452272EBA160D9AE040A8C048230EEB",
> "smtpAddress": "john_at_knowledgemill.com",
> "primary": "true",
> "legacy": "false"
> }}
>
> This is bad - the single element is not coming back as an array.
>
> From my understanding this should have been rectified when using a NATURAL JSON notation. It does not work for me.
>
> I have also tried fixing the arrays specifically with the default MAPPED notation e.g.:
>
> JSONConfiguration.mapped().arrays( NAMES OF ARRAYS HERE).build()

Could you please try with just:

JSONConfiguration.mapped().arrays("UserEmailAddress").build();

And let me know what happens with the single element array case?

~Jakub

>
> This also does not work.
>
> It appears that the ContextResolver is having no effect. However, I have confirmed that the Provider is being loaded on startup.
>
> Any help - much appreciated.
>
> Cheers.
>
> James
>

--
Jakub Podlešák
Software Engineer at SUN Microsystems And CZJUG Co-Leader
http://blogs.sun.com/japod