users@jersey.java.net

Custom Natural JAXBContextResolver Problem - Single Element Arrays

From: James Allchin <james_at_knowledgemill.com>
Date: Mon, 5 Oct 2009 12:25:55 -0700

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 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()

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