Using JAXB with generics

View: New views
2 Messages — Rating Filter:   Alert me  

Using JAXB with generics

by Pratap Singh :: Rate this Message:

| View Threaded | Show Only this Message

Hello helpful Internet citizens

I'm getting to know the internals of Jersey and JAXB on Glassfish, and I've run into a bit of a snag serializing a class that uses generics.  I need to return XML to an ExtJS paginated grid in the following format:

<countedCollection>
    <totalCount>202</totalCount>
    <record version="1">
        <id>25</id>
        <name>Test Client 1</name>
        <baseTag>0025</baseTag>
        <clientStatus>
                <id>1</id>
                <description>Active</description>
        </clientStatus>
    </record>
    <record version="1">
        <id>26</id>
        <name>Test Client 2</name>
        <baseTag>0026</baseTag>
        <clientStatus>
            <id>1</id>
            <description>Active</description>
        </clientStatus>
    </record>
</countedCollection>

The names are fungible, but the imperative thing is that there be a total record count and a list of records.  JAXB serializes collections nicely enough, and if I return a collection filled with JPA entities from a Jersey @GET method, JAXB will serialize the collection, and then Jersey will happily shuttle it across the wire.  Rather than declare a new class for each JPA type, I wrote the following class to encapsulate both the collection and a total records count using generics:

package com.donotuse.facade;

import java.util.*;
import javax.xml.bind.annotation.*;
import com.donotuse.jpa.*;

@XmlRootElement
//@XmlSeeAlso({Client.class})
public class CountedCollection<T> {
        private static final long serialVersionUID = -1483486849810154904L;
       
        @XmlElement
        private long totalCount;
       
        @XmlElement
        private Vector<T> records;
       
        CountedCollection() {

        }
       
        CountedCollection(Collection<T> list, long totalCount) {
                this.records = new Vector<T>();
                this.records.addAll(list);
                this.totalCount = totalCount;
        }
}


The RESTful service calling this code looks like this:

package com.wdru.rest;

import javax.ws.rs.*;
import javax.ejb.*;

import com.wdru.facade.ClientFacadeRemote;
import com.wdru.facade.ClientStatusFacadeRemote;
import com.wdru.facade.CountedCollection;
import com.wdru.jpa.Client;
import com.wdru.jpa.ClientStatus;

@Path("/client")
@Produces("application/xml")
public class ClientRest {
        @EJB
        private ClientFacadeRemote cf;

        @EJB
        private ClientStatusFacadeRemote csf;
       
        public ClientRest() {

        }
        @GET

        public CountedCollection<Client> doGet(@QueryParam("dir") String sort_dir,
                                                             @QueryParam("sort") String sort_field,
                                                             @DefaultValue("0") @QueryParam("start") Integer start,
                                                            @DefaultValue("0") @QueryParam("limit") Integer limit) throws Exception {
                String sd = null;
       
                if (sort_dir!=null) {
                        sd = sort_dir.toUpperCase();
                }
               
                return this.cf.readAll(start, limit, sd, sort_field);
        }
.
.
.

All the entities in com.donotuse.jpa are annotated and serialize correctly.  With the XmlSeeAlso commented out, I get 'javax.xml.bind.JAXBException: class com.donotuse.jpa.Client nor any of its super class is known to this context.' gracing my log files.  When I uncomment the @XmlSeeAlso, it serializes just fine, but having to put a reference to every class that might use this collection kind of defeats the purpose of using generics in the first place.

So my questions is: what is the best way to go about doing this?  Is there some black magic I can use in the wrapper class that tells JAXB to use type T instead of having to specify each type in XmlSeeAlso?  Thanks for all replies, advice, and flames in advance.

Cheers,
Pratap

Re: Using JAXB with generics

by Wolfgang Laun-2 :: Rate this Message:

| View Threaded | Show Only this Message

You want to create XML for <countedCollection> according to the generic class CountedCollection, with an arbitrary class being used for the elements of the contained Vector record. The problem is that JAXB has to be made aware of all the classes potentially occurring as Vector elements.

One way to get what you want might be to use a common superclass AbstractRecord of all the classes in com.donotuse.jpa.* that might appear as record instances instead of the generic parameter for Vector. Or, if there is no gain in having this base class, simply use Object.

JAXB will marshal any type, as long as the class is entered into the JAXBContext; so you might have to create this with
JAXBContext.newInstance( "com.donotuse.facade:com.donotuse.jpa" );

-W


On Mon, Oct 26, 2009 at 6:30 AM, Pratap Singh <p.singh11972@...> wrote:

Hello helpful Internet citizens

I'm getting to know the internals of Jersey and JAXB on Glassfish, and I've
run into a bit of a snag serializing a class that uses generics.  I need to
return XML to an ExtJS paginated grid in the following format:

<countedCollection>
   <totalCount>202</totalCount>
   <record version="1">
       <id>25</id>
       <name>Test Client 1</name>
       <baseTag>0025</baseTag>
       <clientStatus>
               <id>1</id>
               <description>Active</description>
       </clientStatus>
   </record>
   <record version="1">
       <id>26</id>
       <name>Test Client 2</name>
       <baseTag>0026</baseTag>
       <clientStatus>
           <id>1</id>
           <description>Active</description>
       </clientStatus>
   </record>
</countedCollection>

The names are fungible, but the imperative thing is that there be a total
record count and a list of records.  JAXB serializes collections nicely
enough, and if I return a collection filled with JPA entities from a Jersey
@GET method, JAXB will serialize the collection, and then Jersey will
happily shuttle it across the wire.  Rather than declare a new class for
each JPA type, I wrote the following class to encapsulate both the
collection and a total records count using generics:

package com.donotuse.facade;

import java.util.*;
import javax.xml.bind.annotation.*;
import com.donotuse.jpa.*;

@XmlRootElement
//@XmlSeeAlso({Client.class})
public class CountedCollection<T> {
       private static final long serialVersionUID = -1483486849810154904L;

       @XmlElement
       private long totalCount;

       @XmlElement
       private Vector<T> records;

       CountedCollection() {

       }

       CountedCollection(Collection<T> list, long totalCount) {
               this.records = new Vector<T>();
               this.records.addAll(list);
               this.totalCount = totalCount;
       }
}


The RESTful service calling this code looks like this:

package com.wdru.rest;

import javax.ws.rs.*;
import javax.ejb.*;

import com.wdru.facade.ClientFacadeRemote;
import com.wdru.facade.ClientStatusFacadeRemote;
import com.wdru.facade.CountedCollection;
import com.wdru.jpa.Client;
import com.wdru.jpa.ClientStatus;

@Path("/client")
@Produces("application/xml")
public class ClientRest {
       @EJB
       private ClientFacadeRemote cf;

       @EJB
       private ClientStatusFacadeRemote csf;

       public ClientRest() {

       }
       @GET

       public CountedCollection<Client> doGet(@QueryParam("dir") String sort_dir,
                                                            @QueryParam("sort") String sort_field,
                                                            @DefaultValue("0") @QueryParam("start") Integer start,
                                                           @DefaultValue("0") @QueryParam("limit") Integer limit) throws
Exception {
               String sd = null;

               if (sort_dir!=null) {
                       sd = sort_dir.toUpperCase();
               }

               return this.cf.readAll(start, limit, sd, sort_field);
       }
.
.
.

All the entities in com.donotuse.jpa are annotated and serialize correctly.
With the XmlSeeAlso commented out, I get 'javax.xml.bind.JAXBException:
class com.donotuse.jpa.Client nor any of its super class is known to this
context.' gracing my log files.  When I uncomment the @XmlSeeAlso, it
serializes just fine, but having to put a reference to every class that
might use this collection kind of defeats the purpose of using generics in
the first place.

So my questions is: what is the best way to go about doing this?  Is there
some black magic I can use in the wrapper class that tells JAXB to use type
T instead of having to specify each type in XmlSeeAlso?  Thanks for all
replies, advice, and flames in advance.

Cheers,
Pratap
--
View this message in context: http://www.nabble.com/Using-JAXB-with-generics-tp26054767p26054767.html
Sent from the java.net - jaxb users mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...