Convert from JSon to multiple unknown java object types using GSon

Abe picture Abe · Jun 11, 2013 · Viewed 30.2k times · Source

I have a netty decoder which uses GSon to convert JSon coming from web client to appropriate java objects. The requirement is: Client could be sending unrelated classes, Class A, Class B, Class C etc but I would like to use the same singleton decoder instance in the pipeline to do conversion(since I use spring for configuring it). The issue I am facing is I need to know the class object before hand.

public Object decode()
{
    gson.fromJson(jsonString, A.class);
}

This cannot decode B or C. The users of my library, now need to write separate decoders for each class instead of a cast later down the line. The only way I can see for doing this is to pass the String name of the class say "org.example.C" in the JSon string from web client, parse it out in the decoder and then use Class.forName to get the class. Is there a better way to do this?

Answer

PomPom picture PomPom · Jun 13, 2013

GSon MUST know the class matching the json string. If you don't wan't to provide it with fromJson(), you can actually specify it in the Json. A way is to define an interface and bind an adapter on it.

Like :

  class A implements MyInterface {
    // ...
  }

  public Object decode()
  {
    Gson  gson = builder.registerTypeAdapter(MyInterface.class, new MyInterfaceAdapter());
    MyInterface a =  gson.fromJson(jsonString, MyInterface.class);
  }

Adapter can be like :

public final class MYInterfaceAdapter implements JsonDeserializer<MyInterface>, JsonSerializer<MyInterface> {
  private static final String PROP_NAME = "myClass";

  @Override
  public MyInterface deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    try {
      String classPath = json.getAsJsonObject().getAsJsonPrimitive(PROP_NAME).getAsString();
      Class<MyInterface> cls = (Class<MyInterface>) Class.forName(classPath);

      return (MyInterface) context.deserialize(json, cls);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }

    return null;
  }

  @Override
  public JsonElement serialize(MyInterface src, Type typeOfSrc, JsonSerializationContext context) {
    // note : won't work, you must delegate this
    JsonObject jo = context.serialize(src).getAsJsonObject();

    String classPath = src.getClass().getName();
    jo.add(PROP_NAME, new JsonPrimitive(classPath));

    return jo;
  }
}