Monday, April 9, 2012

A JSON Serialization Engine - Part I


In this previous post, I describe a specific JSON format that I want to consume on the client side using Dojo. In this post, I am going to show how to take a Java object and create that JSON format using the JsonStoreEngine.

The JsonStoreEngine is something I have written, has a simple API and works as a black box. The developer passes a Java object into the engine and the engine gives a custom formatted JSON structure back. The JsonStoreEngine also provides flexibility, giving the developer the ability to customize and control how particular objects are converted to JSON.

This version of the JsonStoreEngine is one way. It does not support deserialization. My primary use case for this engine is to serialize an object graph into a Dojo store, so that I can use the data on the client side to build the web page. When communicating back with the server, I only send a limited set of data using the standard HTTP post mechanism.

The Data

This custom JSON format is flat list of items where references to other items use a special notation. The following is an example of that structure:
var dataArr = [
  { id: 'customer::1', _type: 'customer', 
    name: 'Anderson, Jose',
    address: { _reference: 'address::1' } 
  },
  { id: 'address::1', _type: 'address', 
    address1: '2272 Stratford Drive', 
    address2: 'Honolulu, HI 96814' 
  },
  { id: 'customer::2', _type: 'customer', 
    name: 'Gomez, Marcus',
    address: { _reference: 'address::2' } 
  },
  { id: 'address::2', _type: 'address', 
    address1: '2718 College Avenue', 
    address2: 'Dayton, OH 45459' 
  },

  // ... other customers ...

  { id: 'product::1', _type: 'product', 
    name: 'Ball Original Classic Pectin Small Batch', 
    price: 1.07 
  },
  { id: 'product::2', _type: 'product', 
    name: 'Ball 4-Pk 16 Oz. Wide Mouth Class Canning Jars with Lids', 
    price: 4.48 
  },

  // ... other products ...

  { id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', displayOrder: 10 
  },
  { id: 'orderStatus::2', _type: 'orderStatus', 
    name: 'Back Ordered', displayOrder: 30 
  },
  { id: 'orderStatus::3', _type: 'orderStatus', 
    name: 'Shipped', displayOrder: 20 
  },
  { id: 'order::1', _type: 'order', orderNumber: 1234, 
    customer: { _reference: 'customer::1' }, 
    orderStatus: { _reference: 'orderStatus::1' },
    lineItems:[
        { _reference: 'orderLine::1' }, 
        { _reference: 'orderLine::2' }
    ]
  },
  { id: 'orderLine::1', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 1.07, quantity: 10, 
    product: { _reference: 'product::1' } 
  },
  { id: 'orderLine::2', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 4.48, quantity: 1, 
    product: { _reference: 'product::2' } 
  },

  // ... other orders ...
];
The API

The API consists of two methods. The developer can call serialize only passing the object to be serialized. The JSON will be returned as a String to the developer. Alternatively, the developer can also pass an OutputStream. Doing so will write the JSON to that stream and nothing will be returned.
public String serialize(Object model)

public void serialize(OutputStream out, Object model) 
    throws IOException
Customizing the Conversion

The default converter is the PojoConverter. In the example below, a second PojoConverter is registered to handle the OrderLine class. Now anytime an OrderLine object is serialized, this converter will be used. This converter is configured to only serialize two fields: product and price. So looking at the data example above, the order field of an OrderLine object would not be serialized.
JsonStoreEngine engine = new JsonStoreEngine();
  
PojoConverter productConverter = new PojoConverter();
productConverter.setIncludeFieldsByDefault(false);
productConverter.setOverriddenFields(
  new String[] { "product", "price" });
engine.getLookupMap().put(OrderLine.class, productConverter);
The developer can also create a custom implementation of the JsonConverter interface. This custom implementation can then be registered in the same manner as the as the PojoConverter above.

The code for the serialization engine is checked into the evince framework repository on GitHub. In part II, I will look into the black box and go over the details of how the engine works.

No comments:

Post a Comment