Showing posts with label JSON. Show all posts
Showing posts with label JSON. Show all posts

Friday, November 9, 2012

J2EE Application with MOXy

As part of my refactoring of my existing J2EE application using JAX-RS, I wanted to try and reduce the JAXB custom code in place. Previously I was using Jackson and Jersey for JSON serialization using the "Mapped" notation. This involved creating my own JAXBContext object and I was required to create some special mapping processing so that I could map an attribute as etiher an array, non-string or other value.

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 can read more on the MOXy settings here.

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>
There are other ways to configure the MOXy configuration, but this is the one that I choose and seemed to fit well for my environment (since I was using a 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;
In the 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
The first oddity was I could not define a 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;
 }
}
The resulting JSON will thus look as I intended:
{
    "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.




Thursday, March 26, 2009

Using JSON with JAXB in new Jersey 1.0.2

Jersey 1.0.2 was released in early February and I just got around to updating to it. I was quite excited to read about the new support for JAXB marshalling to JSON formats (this was introduced I believe in an earlier release but not well detailed). Jakub Podlesak has some nice articles on this functionality on his blog and would recommend reading them. One thing I found, is initially I attempted to use the MAPPED_JETTISON notation in the JSONConfiguration which worked fine with Java JDK 1.6u10. However when I changed this to Natural notation to support my GXT application expected output, I recieved the following error:

javax.xml.bind.JAXBException: property "retainReferenceToInfo" is not supported 
   at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:63) 
   ...


Upon re-reading the dependency notes of Jersey, I noticed the following note: "This module depends on the JAXB reference implementation version 2.1.10 or greater, and such a version is required when enabling support for the JAXB natural JSON convention..." . The version of JAXB shipped with JDK 1.6 is not at a high enough level to support this functionality. Fortuantely the workaround to get this to work is pretty easy if you following the following steps:
  1. Download the JAXB jar from the Jersey depenendencies: jaxb-api-2.1.jarand save this to a local location.
  2. Copy the jaxb-api-2.1.jar to the jre/lib/endorsed folder of your JRE or JDK installation. You may need administrative privileges on some platforms.
  3. Restart eclipse (or other IDE).
  4. In your project class path, include the other JAXB jar files listed under the "non maven developers require... section of the JAXB portion of the document

Information on the endorsed libraries is available here.

Tuesday, May 13, 2008

Flexjson limitations

The old saying goes "If it sounds too good to be true, it is too good to be true". Well this applied to Flexjson. While this tool is very capable of externalizing an object to the JSON format, there are several shortcomings which make it difficult to use at the moment. The contributors have mentioned they are working to address these issues. Currently Flexjson only can process primitives, wrappers, Strings and Dates. Objects (that are not collections) are they themselves externalized into JSON. In complex data models, you may not wish to serialize the entire downstream object. You may wish to only serialize it's ID. This (and my gripping about the implementation) is shown in one of my previous posts on JAXB and XmlID. Currently Flexjson has no clean way of supporting this. The only was to attempt this would be through the usage of include() and exclude() on the JSONSerializer. The downside of this, is that you essentially need a specific handler for each object you want to serialize since the attributes/conditions of inclusion or exclusion will change. Lets look at an example:
@Entity
public class Stamp implements Serializable {
  private Long id;
  private String description;
  private Country country;
  // ... other attributes and methods
}

@Entity
public class Country implements Serializable {
   private Long id;
   private String name;
   // ...
}
So in this example, if we wanted to serialize all of the Stamps to JSON, by default the country would be serialized for each stamp. In a typical system, we might have 200 countries and 50,000 stamps. This means that our countries are fully serilized in a redundant fashion many times over. In this situation what we really want is the country id field. We can get this in the following way:
  Writer out = // ... some writer like a PrintWriter
  Collection stamps = stampService.getAll( );
  JSONSerializer serializer = new JSONSerializer( );
  serializer = serializer.include("id","description","country.id").exclude("*");
  for( Stamp s: stamps ) { 
     out.write( serializer.serialize( s ) );
  }

While this works, If your object has many properties and object relationships, this can get a little exhaustive setting up the include and exclude parameters. It also means you either (a) need a introspective tool to read this from your beans or (b) you need to provide some handler for each bean to setup the includes and excludes properly. I personally have great faith in the Open Source community, and will look forward to leveraging the next version of Flexjson to cleaner handle this situation with a Transformer (Transformers today only handle Strings, primitives and dates). Until then I suppose I'll have to come up with some solution that is tied to my object model.

Saturday, May 10, 2008

JSON Annotating of Java Beans

As I was working through a GWT Code Serializer for generating Java serialization code to serialize objects as XML or JSON, I started thinking about how nice it was to leverage JPA Annotations in documenting JSON serialization on persistent beans. Wouldn't it be nice to serialize objects to JSON as easily as we can do with JAXB? After searching the WEB for a solution, I was almost about ready to go ahead and develop an Annotation infrastructure for this very problem. That was when I came across Flexjson, which does exactly this. In fact, unlike JAXB's marshalling terminology, Flexjson actually calls it serialization. As of yet, I have not tried Flexjson, but from looking at the examples shown in the link above, it seems to do exactly what one would expect.