Wednesday, April 11, 2012

A JSON Serialization Engine - Part II


In part I, I introduced the JsonStoreEngine and described how the developer uses the engine to serialize Java objects into JSON and can customize how the engine serializes certain objects. In this post, I am going to show more of the inner workings of the JsonStoreEngine.

Under the hood, the JsonStoreEngine uses Jackson for the JSON serialization. The JsonStoreEngine provides the logic to emit the flat list of items using the reference object notation to represent object references.

A Simple Example
public class OrderStatus {
    
    public String getIdentifier();
    
    public String getName();
    
    public String getDisplayOrder();
}

public class OrderStatusJsonConverter 
 extends AbstractTypedJsonConverter<OrderStatus> {

    public String determineIdentifier(OrderStatus obj) {
        return String.format("orderStatus::%s", obj.getIdentifier()); 
    }
    
    protected String onDetermineType(OrderStatus obj);
        return "orderStatus";
    }
    
    protected void onWriteObjectProperties(
      JsonSerializationContext context, OrderStatus obj) 
      throws IOException;
      
        context.writeProperty("name", obj.getName());
        context.writeProperty("order", obj.getDisplayOrder());
    }
}
The above example is the most basic. An OrderStatus object has no references to other objects and the engine serializes the primitive properties. This example produces the following output:
[{ id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', order: 10 
}]

A Bigger Example
Here is an example where the engine serializes object references.
public class Order {
    
    public String getIdentifier();

    public String getOrderNumber();
    
    public Customer getCustomer();
    
    public OrderStatus getOrderStatus();

    public List getOrderLineItems();
}

public class OrderJsonConverter 
 extends AbstractTypedJsonConverter<Order> {

    public String determineIdentifier(Order obj) {
        return String.format("order::%s", obj.getIdentifier()); 
    }
    
    protected String onDetermineType(Order obj);
        return "order";
    }
    
    protected void onWriteObjectProperties(
      JsonSerializationContext context, Order obj) 
      throws IOException;
      
        context.writeProperty("orderNumber", obj.getOrderNumber());
        context.writeProperty("customer", obj.getCustomer());
        context.writeProperty("orderStatus", obj.getOrderStatus());
        context.writeProperty("lineItems", obj.getOrderLineItems());
    }
}
And the output produced:
[{ id: 'order::1', _type: 'order', orderNumber: 1234, 
    customer: { _reference: 'customer::1' }, 
    orderStatus: { _reference: 'orderStatus::1' },
    lineItems:[
        { _reference: 'orderLine::1' }, 
        { _reference: 'orderLine::2' }
    ]
},
{ id: 'customer::1', _type: 'customer', 
    name: 'Anderson, Jose'
},
{ id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', order: 10 
},
{ 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' } 
}]

Where the Engine Does the Work
context.writeProperty("orderStatus", obj.getOrderStatus());
When the converter asks to write the order status, the engine intercepts and emits the reference object. The engine then registers the object for deferred serialization, only if the object hasn't already been serialized or registered for deferred serialization. Once the serialization of the original object is complete, the engine then serializes the objects that had been deferred.

Using the JsonStoreEngine and the ViewModelStore, we now have a framework in place to ship a complex model of data that is represented by Java objects on the server to javascript objects that can be consumed on the web client and used as a view model. The complexity of the data model can include references to other objects, circular object references and self references and also gives us control to specify how certain objects are serialized.

This will be a core component to building a Dojo View to plug into Spring's Web MVC architecture. The code for the serialization engine is checked into the evince framework repository on GitHub. There is also some more documentation on how to customize the serialization and conversion process.

No comments:

Post a Comment