Monday, September 3, 2012

Serializing a JBox2D Shape with GSON

For JBox2D 2.1.2, The JBox2D Shape is an abstract class which does not have a default no-arg constructor.  Serializing it with GSON works fine, but trying to deserialize it will throw the following exception:

java.lang.RuntimeException: 
Failed to invoke public org.jbox2d.collision.shapes.Shape() with no args ...
Caused by: java.lang.InstantiationException ...

The Shape constructor takes a ShapeType argument - this is stored as the field m_type.  In order to deserialize a Shape you need to read the m_type field out of the JSON string and instantiate the concrete class based on the ShapeType.  The code to do this is given below:


package com.exposure101.ubik.shared.io.json;

import java.lang.reflect.Type;

import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeType;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class ShapeTypeAdapter implements JsonDeserializer<Shape> {
  
  @Override
  public Shape deserialize(JsonElement element, Type type, 
        JsonDeserializationContext context) throws JsonParseException {
    final JsonObject wrapper = (JsonObject) element;
    final ShapeType shapeType = ShapeType.valueOf(wrapper.get("m_type").getAsString());
    Type actualType = getClass(shapeType);
    return context.deserialize(element, actualType);
  }

  private Class<? extends Shape> getClass(ShapeType shapeType) {
    switch (shapeType) {
    case CIRCLE:
      return CircleShape.class;
    case POLYGON:
      return PolygonShape.class;
    default:
      return null;
    }
  }
}