Since I was going to use the latest version of eclipselink with the refactoring (currently EclipseLink 2.4.1) I decided to check out using MOXy which I had always had my eye on. My goal was to avoid the mapping approach (which was inherently buggy since it required me to remember to add any field to a property file for proper serialization and I could not serialize the same named field differently for different types - ie. field X was always to be type Y). MOXy seemed to like it might be able to address these concerns.
To leverage MOXy in your environment, you need to include it as a provider. In my case I included it in my JAX-RS Application class and set some configuration properties on it. Here is an example of my current application (note I am using Package scanning for JAX-RS resources/providers in Jersey)
public class WebApplication extends PackagesResourceConfig { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(WebApplication.class.getName()); public WebApplication() { super("org.javad.web.providers","org.javad.preferences.model.resources","org.javad.stamp.model.resources"); } @Override public Set<Object> getSingletons() { MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider(); moxyJsonProvider.setAttributePrefix(""); moxyJsonProvider.setFormattedOutput(true); moxyJsonProvider.setIncludeRoot(false); moxyJsonProvider.setMarshalEmptyCollections(true); moxyJsonProvider.setValueWrapper("$"); Set<Object> set = new HashSet<Object>(); set.add(moxyJsonProvider); return set; } }
You then need to reference your Application in your
web.xml
as part of your JAX-RS resource mapping:<init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>org.javad.web.WebApplication</param-value> </init-param>
PackageResourceConfig
from Jersey for finding my resources).Since several of my JPA entities included a foreign reference to another JPA entity, I wanted only the ID of the foreign entity to be serialized. Previously I had been using the
@XmlIDRef
to drive this for serialization, but I had difficulty using this with MOXy. Instead, I needed to declare an XML Adapter to do the serialization and deserialization of the field. This is done using the @XmlJavaTypeAdapter
annotation. For my album entity, the foreign key reference to the stamp collection entity looks like this:@XmlJavaTypeAdapter(StampCollectionRefAdapter.class) @XmlElement(name = StampFormConstants.STAMP_COLLECTION_REF ) @ManyToOne(optional=false,fetch=FetchType.EAGER) @JoinColumn(name="COLLECTION_ID",nullable=false) private StampCollection collection;
StampCollectionRefAdapter
I transfer the StampCollection entity to an ID and vice versa (when deserializing from a JSON posting). One issue I ran into was that I could not inject the StampCollectionService into this class. This is because these classes are initialized by MOXy (it would appear) prior to the CDI and EntityManagerFactory initialization, so I needed to obtain the EntityManagerFactory which was used in my initialization servlet listener (this sets up a few configurations in my environment). I had tried declaring this class as a @Stateless
EJB but did not have any luck (likely because it was constructed explicitly). I may look into this further later, but for now the only way I could get access to the service to transfer the ID back to the physical entity was to leveage the entity manager factory from the initialization listener through a static context (note: this would might fail if I tried to perform any transactions in against this, but should work for normal queries)public class StampCollectionRefAdapter extends XmlAdapter<StampCollectionRefAdapter.StampCollectionRefType, StampCollection> { public StampCollectionRefAdapter() { super(); } @Override public StampCollection unmarshal(StampCollectionRefType v) throws Exception { StampCollection collection = null; EntityManagerFactory emf = SessionInitializer.getEntityManagerFactory(); if( emf != null ) { EntityManager em = emf.createEntityManager(); if( em != null ) { collection = em.find(StampCollection.class, v.id); } } return collection; } @Override public StampCollectionRefType marshal(StampCollection v) throws Exception { StampCollectionRefType t = new StampCollectionRefType(); t.id = v.getId().longValue(); return t; } public static class StampCollectionRefType { @XmlValue public long id; } }
I am having a small issue with arrays of Entities. I have a serializable class which is Genericized and has two fields:
- total - representing the total number of objects (not necessarily the items returned)
- items - the List
of entities to be returned
getItems()
method on the abstract class. Instead I had to declare it on the concrete class that implements the Generic type. The reason for this is I want my array of items to be the plural of the actual items contained within it. This requires me to declare the collection similiar to the following:@XmlRootElement(name="list") @XmlAccessorType(XmlAccessType.PROPERTY) public class AlbumModelList extends AbstractModelList<Album> { @Override @XmlElement(name="albums") public List<Album> getItems() { return items; } }
{ "total": 5, "albums": [ { "id": "2000", "name": "Test Collection X", "countryRefs": [], "stampCollectionRef": 1 }, { "id": "1", "name": "Test Collection x2", "countryRefs": [], "stampCollectionRef": 1 }, { "id": "2", "name": "Test Collection x4", "countryRefs": [], "stampCollectionRef": 1 }, { "id": "3", "name": "Test Collection x5", "countryRefs": [], "stampCollectionRef": 1 }, { "id": "4", "name": "Test Collection x6", "countryRefs": [], "stampCollectionRef": 1 } ] }But if I use a content type of XML (which I am not using very often) the Array is not showing up as am
<albums/>
element that contains <album/>
nodes but instead looks like the following:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <list> <total>5</total> <albums id="2000"> <name>Test Collection X</name> <stampCollectionRef>1</stampCollectionRef> </albums> <albums id="1"> <name>Test Collection x2</name> <stampCollectionRef>1</stampCollectionRef> </albums> <albums id="2"> <name>Test Collection x4</name> <stampCollectionRef>1</stampCollectionRef> </albums> <albums id="3"> <name>Test Collection x5</name> <stampCollectionRef>1</stampCollectionRef> </albums> <albums id="4"> <name>Test Collection x6</name> <stampCollectionRef>1</stampCollectionRef> </albums> </list>
Not sure what I am missing here, but I need to address this at some point along with modifying my entity IDs from wrapper types to primitives.